-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[css-typed-om] Refactor the per-property test harness.
This patch refactors the per-property test harness quite a bit to deal with property specific behaviour: - We allow tests to override the expectation/asserts for specified and computed values. So it means that a property might compute 'auto' to '0px' and we can easily assert that by passing a callback to 'computed'. - We moved margin-top to margin and test all the margin properties (except the margin shorthand). - We made margin properties work by setting the correct metadata in the CSSProperties.json5. - We removed unitless zero tests. They don't seem to be required by the spec. Bug: 774887 Change-Id: I08605ac6af01576ff9f6c878c2ca9e280c9948e1
- Loading branch information
1 parent
1e4fe87
commit 6be85c3
Showing
3 changed files
with
144 additions
and
85 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
197 changes: 125 additions & 72 deletions
197
css/css-typed-om/the-stylepropertymap/properties/resources/testsuite.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,115 +1,168 @@ | ||
const gTestSyntax = { | ||
'0': { | ||
description: 'unitless zero', | ||
set: true, | ||
examples: [ | ||
new CSSUnitValue(0, 'number'), | ||
], | ||
}, | ||
const gTestSyntaxExamples = { | ||
'<length>': { | ||
description: 'a length', | ||
get: true, | ||
set: true, | ||
examples: [ | ||
new CSSUnitValue(0, 'px'), | ||
new CSSUnitValue(-3.14, 'em'), | ||
new CSSUnitValue(3.14, 'cm'), | ||
{ | ||
description: "zero px", | ||
input: new CSSUnitValue(0, 'px') | ||
}, | ||
{ | ||
description: "a negative em", | ||
input: new CSSUnitValue(-3.14, 'em'), | ||
defaultComputed: value => { | ||
// 'ems' are relative units, so just check that it computes to px | ||
assert_class_string(value, 'CSSUnitValue', | ||
'"em" lengths must compute to a CSSUnitValue'); | ||
assert_equals(value.unit, 'px', 'unit'); | ||
} | ||
}, | ||
{ | ||
description: "a positive cm", | ||
input: new CSSUnitValue(3.14, 'cm'), | ||
defaultComputed: value => { | ||
// 'cms' are relative units, so just check that it computes to px | ||
assert_class_string(value, 'CSSUnitValue', | ||
'"cm" lengths must compute to a CSSUnitValue'); | ||
assert_equals(value.unit, 'px', 'unit'); | ||
} | ||
}, | ||
], | ||
}, | ||
'<percentage>': { | ||
description: 'a percent', | ||
get: true, | ||
set: true, | ||
examples: [ | ||
new CSSUnitValue(0, 'percent'), | ||
new CSSUnitValue(-3.14, 'percent'), | ||
new CSSUnitValue(3.14, 'percent'), | ||
{ | ||
description: "zero percent", | ||
input: new CSSUnitValue(0, 'percent') | ||
}, | ||
{ | ||
description: "a negative percent", | ||
input: new CSSUnitValue(-3.14, 'percent') | ||
}, | ||
{ | ||
description: "a positive percent", | ||
input: new CSSUnitValue(3.14, 'percent') | ||
}, | ||
], | ||
}, | ||
'<time>': { | ||
description: 'a time', | ||
get: true, | ||
set: true, | ||
examples: [ | ||
new CSSUnitValue(0, 's'), | ||
new CSSUnitValue(-3.14, 'ms'), | ||
new CSSUnitValue(3.14, 's'), | ||
{ | ||
description: "zero seconds", | ||
input: new CSSUnitValue(0, 's') | ||
}, | ||
{ | ||
description: "negative milliseconds", | ||
input: new CSSUnitValue(-3.14, 'ms') | ||
}, | ||
{ | ||
description: "positive seconds", | ||
input: new CSSUnitValue(3.14, 's') | ||
}, | ||
], | ||
}, | ||
'<ident>': { | ||
description: 'a CSSKeywordValue', | ||
set: true, | ||
get: true, | ||
// user-specified examples | ||
examples: null, | ||
}, | ||
}; | ||
|
||
function testGet(propertyName, values, description) { | ||
// Test setting a value in a style map and then getting it from the inline and | ||
// computed styles. | ||
function testPropertyValid(propertyName, examples, specified, computed, description) { | ||
test(t => { | ||
let element = createDivWithStyle(t); | ||
let styleMap = element.attributeStyleMap; | ||
|
||
for (const styleValue of values) { | ||
element.style[propertyName] = styleValue.toString(); | ||
|
||
getComputedStyle(element); // Force a style recalc. | ||
const result = styleMap.get(propertyName); | ||
assert_style_value_equals(result, styleValue); | ||
} | ||
}, `Can get ${description} from '${propertyName}'`); | ||
} | ||
|
||
function testSet(propertyName, values, description) { | ||
test(t => { | ||
let element = createDivWithStyle(t); | ||
let styleMap = element.attributeStyleMap; | ||
for (const example of examples) { | ||
element.attributeStyleMap.set(propertyName, example.input); | ||
|
||
for (const styleValue of values) { | ||
styleMap.set(propertyName, styleValue); | ||
// specified style | ||
const specifiedResult = element.attributeStyleMap.get(propertyName); | ||
if (specified || example.defaultSpecified) { | ||
(specified || example.defaultSpecified)(specifiedResult); | ||
} else { | ||
assert_not_equals(specifiedResult, null, | ||
'Specified value must not be null'); | ||
assert_true(specifiedResult instanceof CSSStyleValue, | ||
'Specified value must be a CSSStyleValue'); | ||
assert_style_value_equals(specifiedResult, example.input, | ||
`Setting ${example.description} and getting its specified value`); | ||
} | ||
|
||
getComputedStyle(element); // Force a style recalc. | ||
assert_equals(element.style[propertyName], styleValue.toString()); | ||
// computed style | ||
const computedResult = element.computedStyleMap().get(propertyName); | ||
if (computed || example.defaultComputed) { | ||
(computed || example.defaultComputed)(computedResult); | ||
} else { | ||
assert_not_equals(computedResult, null, | ||
'Computed value must not be null'); | ||
assert_true(computedResult instanceof CSSStyleValue, | ||
'Computed value must be a CSSStyleValue'); | ||
assert_style_value_equals(computedResult, example.input, | ||
`Setting ${example.description} and getting its computed value`); | ||
} | ||
} | ||
}, `Can set '${propertyName}' to ${description}`); | ||
} | ||
|
||
function testSetInvalid(propertyName, values, description) { | ||
// Test that styleMap.set throws for invalid values | ||
function testPropertyInvalid(propertyName, examples, description) { | ||
test(t => { | ||
let element = createDivWithStyle(t); | ||
let styleMap = element.attributeStyleMap; | ||
|
||
for (const styleValue of values) { | ||
assert_throws(new TypeError(), () => styleMap.set(propertyName, styleValue)); | ||
let styleMap = createInlineStyleMap(t); | ||
for (const example of examples) { | ||
assert_throws(new TypeError(), () => styleMap.set(propertyName, example.input)); | ||
} | ||
}, `Setting '${propertyName}' to ${description} throws TypeError`); | ||
} | ||
|
||
function createKeywordExample(keyword) { | ||
return { | ||
description: `the '${keyword}' keyword`, | ||
examples: [ { input: new CSSKeywordValue(keyword) } ] | ||
}; | ||
} | ||
|
||
// Run a battery of StylePropertyMap tests on |propertyName|. | ||
// Second argument is a list of test cases. A test case has the form: | ||
// | ||
// { | ||
// syntax: "<length>", | ||
// specified: /* a callback */ (optional) | ||
// computed: /* a callback */ (optional) | ||
// } | ||
// | ||
// If a callback is passed to |specified|, then the callback will be passed | ||
// the result of calling get() on the inline style map (specified values). | ||
// The callback should check if the result is expected using assert_* functions. | ||
// If no callback is passed, then we assert that the result is the same as | ||
// the input. | ||
// | ||
// Same goes for |computed|, but with the computed style map (computed values). | ||
function runPropertyTests(propertyName, testCases) { | ||
let productionsTested = new Set(); | ||
let syntaxTested = new Set(); | ||
|
||
for (const testCase of testCases) { | ||
const syntax = gTestSyntax[testCase.specified]; | ||
if (!syntax) | ||
throw new Error(`'${testCase.specified}' is not a valid production`); | ||
// Retrieve test examples for this test case's syntax. If the syntax | ||
// looks like a keyword, then create an example on the fly. | ||
const syntaxExamples = testCase.syntax.match(/^[a-z\-]+$/) ? | ||
createKeywordExample(testCase.syntax) : | ||
gTestSyntaxExamples[testCase.syntax]; | ||
|
||
const examples = testCase.examples || syntax.examples; | ||
if (!examples) | ||
throw new Error(`'${testCase.specified}' tests require explicit examples`); | ||
if (!syntaxExamples) | ||
throw new Error(`'${testCase.syntax}' is not a valid CSS component`); | ||
|
||
if (syntax.get) | ||
testGet(propertyName, examples, syntax.description); | ||
if (syntax.set) | ||
testSet(propertyName, examples, syntax.description); | ||
testPropertyValid(propertyName, | ||
syntaxExamples.examples, | ||
testCase.specified, | ||
testCase.computed, | ||
syntaxExamples.description); | ||
|
||
productionsTested.add(testCase.specified); | ||
syntaxTested.add(testCase.syntax); | ||
} | ||
|
||
// Also test that styleMap.set rejects invalid CSSStyleValues. | ||
for (const [production, syntax] of Object.entries(gTestSyntax)) { | ||
if (!productionsTested.has(production)) { | ||
if (syntax.set && syntax.examples) | ||
testSetInvalid(propertyName, syntax.examples, syntax.description); | ||
for (const [syntax, syntaxExamples] of Object.entries(gTestSyntaxExamples)) { | ||
if (!syntaxTested.has(syntax)) { | ||
testPropertyInvalid(propertyName, | ||
syntaxExamples.examples, | ||
syntaxExamples.description); | ||
} | ||
} | ||
} |