diff options
Diffstat (limited to 'testing/web-platform/tests/css/support/numeric-testcommon.js')
-rw-r--r-- | testing/web-platform/tests/css/support/numeric-testcommon.js | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/testing/web-platform/tests/css/support/numeric-testcommon.js b/testing/web-platform/tests/css/support/numeric-testcommon.js new file mode 100644 index 0000000000..3a39e2a973 --- /dev/null +++ b/testing/web-platform/tests/css/support/numeric-testcommon.js @@ -0,0 +1,196 @@ +'use strict'; + +/* +Provides functions to help test that two numeric values are equivalent. +These *do not* rely on you predicting what one value will serialize to; +instead, they set and serialize *both* values, +and just ensure that they serialize to the same thing. + +They rely on a #target element existing in the document, +as this might rely on layout to resolve styles, +and so it needs to be in the document. + +Three main functions are defined, with the same signatures: +test_math_used() (for testing used values), +test_math_computed() (for testing computed values), +and test_math_specified() (for testing specified values). +Signature for all is: + +test_math_X( + testString, // A numeric value; required. + expectedString, // A hopefully-equivalent numeric value; required. + { // all of these are optional + type, // "number", "length", etc. See impl for full list. Defaults to "length". + msg, // The message to display for the test; autogenned if not provided. + msgExtra, // Extra info to put after the auto-genned message. + prop, // If you want to override the automatic choice of tested property. + extraStyle, // Styles that need to be set at the same time to properly test the value. + } +); + + +Additionally, five specialized functions are provided +to test that a given value is ±∞, ±0, or NaN: + +* test_plus_infinity(testString) +* test_minus_infinity(testString) +* test_plus_zero(testString) +* test_minus_zero(testString) +* test_nan(testString) + +*/ + + + +function test_math_used(testString, expectedString, {approx, msg, msgExtra, type, prop, prefix, suffix, extraStyle={}}={}) { + if(type === undefined) type = "length"; + if(!prop) { + switch(type) { + case "number": prop = "transform"; prefix="scale("; suffix=")"; break; + case "integer": prop = "z-index"; extraStyle.position="absolute"; break; + case "length": prop = "margin-left"; break; + case "angle": prop = "transform"; prefix="rotate("; suffix=")"; break; + case "time": prop = "transition-delay"; break; + case "resolution": prop = "image-resolution"; break; + case "flex": prop = "grid-template-rows"; break; + default: throw Exception(`Value type '${type}' isn't capable of math.`); + } + + } + _test_math({stage:'used', testString, expectedString, type, approx, msg, msgExtra, prop, prefix, suffix, extraStyle}); +} + +function test_math_computed(testString, expectedString, {approx, msg, msgExtra, type, prop, prefix, suffix, extraStyle={}}={}) { + if(type === undefined) type = "length"; + if(!prop) { + switch(type) { + case "number": prop = "transform"; prefix="scale("; suffix=")"; break; + case "integer": prop = "z-index"; extraStyle.position="absolute"; break; + case "length": prop = "flex-basis"; break; + case "angle": prop = "transform"; prefix="rotate("; suffix=")"; break; + case "time": prop = "transition-delay"; break; + case "resolution": prop = "image-resolution"; break; + case "flex": prop = "grid-template-rows"; break; + default: throw Exception(`Value type '${type}' isn't capable of math.`); + } + + } + _test_math({stage:'computed', testString, expectedString, type, approx, msg, msgExtra, prop, prefix, suffix, extraStyle}); +} + +function test_math_specified(testString, expectedString, {approx, msg, msgExtra, type, prop, prefix, suffix, extraStyle={}}={}) { + if(type === undefined) type = "length"; + const stage = "specified"; + if(!prop) { + switch(type) { + case "number": prop = "transform"; prefix="scale("; suffix=")"; break; + case "integer": prop = "z-index"; extraStyle.position="absolute"; break; + case "length": prop = "flex-basis"; break; + case "angle": prop = "transform"; prefix="rotate("; suffix=")"; break; + case "time": prop = "transition-delay"; break; + case "resolution": prop = "image-resolution"; break; + case "flex": prop = "grid-template-rows"; break; + default: throw Exception(`Value type '${type}' isn't capable of math.`); + } + + } + // Find the test element + const testEl = document.getElementById('target'); + if(testEl == null) throw "Couldn't find #target element to run tests on."; + // Then reset its styles + testEl.style = ""; + for(const p in extraStyle) { + testEl.style[p] = extraStyle[p]; + } + if(!msg) { + msg = `${testString} should be ${stage}-value-equivalent to ${expectedString}`; + if(msgExtra) msg += "; " + msgExtra; + } + let t = testString; + let e = expectedString; + if(prefix) { + t = prefix + t; + e = prefix + e; + } + if(suffix) { + t += suffix; + e += suffix; + } + test(()=>{ + testEl.style[prop] = ''; + testEl.style[prop] = t; + const usedValue = testEl.style[prop]; + assert_not_equals(usedValue, '', `${testString} isn't valid in '${prop}'; got the default value instead.`); + testEl.style[prop] = ''; + testEl.style[prop] = e; + const expectedValue = testEl.style[prop]; + assert_not_equals(expectedValue, '', `${expectedString} isn't valid in '${prop}'; got the default value instead.`) + assert_equals(usedValue, expectedValue, `${testString} and ${expectedString} serialize to the same thing in ${stage} values.`); + }, msg); +} + +/* +All of these expect the testString to evaluate to a <number>. +*/ +function test_plus_infinity(testString) { + test_math_used(testString, "calc(infinity)", {type:"number"}); +} +function test_minus_infinity(testString) { + test_math_used(testString, "calc(-infinity)", {type:"number"}); +} +function test_plus_zero(testString) { + test_math_used(`calc(1 / ${testString})`, "calc(infinity)", {type:"number"}); +} +function test_minus_zero(testString) { + test_math_used(`calc(1 / ${testString})`, "calc(-infinity)", {type:"number"}); +} +function test_nan(testString) { + // Make sure that it's NaN, not an infinity, + // by making sure that it's the same value both pos and neg. + test_math_used(testString, "calc(NaN)", {type:"number"}); + test_math_used(`calc(-1 * ${testString})`, "calc(NaN)", {type:"number"}); +} + + +function _test_math({stage, testEl, testString, expectedString, type, approx, msg, msgExtra, prop, prefix, suffix, extraStyle}={}) { + // Find the test element + if(!testEl) testEl = document.getElementById('target'); + if(testEl == null) throw "Couldn't find #target element to run tests on."; + // Then reset its styles + testEl.style = ""; + for(const p in extraStyle) { + testEl.style[p] = extraStyle[p]; + } + if(!msg) { + msg = `${testString} should be ${stage}-value-equivalent to ${expectedString}`; + if(msgExtra) msg += "; " + msgExtra; + } + let t = testString; + let e = expectedString; + if(prefix) { + t = prefix + t; + e = prefix + e; + } + if(suffix) { + t += suffix; + e += suffix; + } + test(()=>{ + testEl.style[prop] = ''; + const defaultValue = getComputedStyle(testEl)[prop]; + testEl.style[prop] = t; + const usedValue = getComputedStyle(testEl)[prop]; + assert_not_equals(usedValue, defaultValue, `${testString} isn't valid in '${prop}'; got the default value instead.`); + testEl.style[prop] = ''; + testEl.style[prop] = e; + const expectedValue = getComputedStyle(testEl)[prop]; + assert_not_equals(expectedValue, defaultValue, `${expectedString} isn't valid in '${prop}'; got the default value instead.`) + if(approx && (type == "number" || type == "angle")){ + let parsedUsed = usedValue.split('(')[1].split(')')[0].split(',').map(parseFloat); + let parsedExpected = expectedValue.split('(')[1].split(')')[0].split(',').map(parseFloat); + assert_array_approx_equals(parsedUsed, parsedExpected, approx, `${testString} and ${expectedString} ${approx} serialize to the same thing in ${stage} values.`); + } else { + assert_equals(usedValue, expectedValue, `${testString} and ${expectedString} serialize to the same thing in ${stage} values.`); + } + }, msg); +} |