blob: 4091b92f3b5eea86019e9c3c035b7f7d34ad4c38 [file] [log] [blame]
/**
* A standard set of tests for using a single property in an
* inline StylePropertyMap
* (https://www.w3.org/TR/css-typed-om-1/#the-stylepropertymap).
*
* Create a config object containing {
* validKeywords: array of strings,
* validObjects: array of CSSStyleValue instances that are valid for the
* property,
* validStringMappings: object containing a mapping of string to
* CSSStyleValues for things expressable in string CSS, but are expressed
* the same as something else in Typed OM.
* supportsMultiple: boolean; whether the property supports a list of
* properties,
* separator: string; for list-valued properties, what string is used to
* separate each item in the list. Defaults to ', '.
* invalidObjects: array of CSSStyleValue instances that are invalid for the
* property
* }
*
* Then, call runInlineStylePropertyMapTests to test the behavior of an inline
* StylePropertyMap for one CSS property for an element.
*
* Note that CSS-wide keywords, and non-CSSStyleValue invalid types do not need
* to be specified.
*
* If necessary, you can continue to define non-standard tests in your html
* file as usual.
*/
function runInlineStylePropertyMapTests(config) {
let element = document.createElement('div');
document.documentElement.appendChild(element);
if (!config.validKeywords) {
throw new Error('Must specify valid keywords (may be the empty list if ' +
'only css-wide keywords apply)');
}
let validKeywords = config.validKeywords.concat([
// CSS-wide keywords
'initial',
'inherit',
'unset'
]);
let validObjects = config.validObjects;
let validStringMappings = config.validStringMappings ?
config.validStringMappings : {};
let invalidObjects = config.invalidObjects.concat([
// No properties should take these values
null,
undefined,
true,
false,
1,
'hello',
{},
new CSSKeywordValue('notAKeyword')
]);
let validObject = validObjects.length ?
validObjects[0] : new CSSKeywordValue(validKeywords[0]);
let styleMap = element.attributeStyleMap;
runSetterTests(
config.property, validKeywords, validObjects, invalidObjects, element);
runGetterTests(config.property, validKeywords, validObjects,
validStringMappings, element);
runGetAllTests(
config.property, validObject, element, config.supportsMultiple);
runDeletionTests(config.property, validObject, element);
runGetPropertiesTests(config.property, validObject, element);
if (config.supportsMultiple) {
const separator = config.separator || ', ';
runSequenceSetterTests(
config.property, validObject, invalidObjects[0], element, separator);
runAppendTests(
config.property, validObject, invalidObjects[0], element, separator);
} else {
runMultipleValuesNotSupportedTests(
config.property, validObject, element);
}
}
function runSetterTests(
propertyName, validKeywords, validObjects, invalidObjects, element) {
for (let keyword of validKeywords) {
test(function() {
element.style = '';
element.attributeStyleMap.set(propertyName, new CSSKeywordValue(keyword));
assert_equals(element.style[propertyName], keyword);
// Force a style recalc to check for crashes in style recalculation.
getComputedStyle(element)[propertyName];
assert_equals(element.style[propertyName], keyword);
}, 'Setting ' + propertyName + ' to ' + keyword);
}
for (let validObject of validObjects) {
test(function() {
element.style = '';
element.attributeStyleMap.set(propertyName, validObject);
assert_equals(element.style[propertyName], validObject.toString());
// Force a style recalc to check for crashes in style recalculation.
getComputedStyle(element)[propertyName];
assert_equals(element.style[propertyName], validObject.toString());
}, 'Setting ' + propertyName + ' to ' + validObject.constructor.name +
' with value ' + validObject.toString());
}
// Negative tests
for (let invalidObject of invalidObjects) {
let name = invalidObject instanceof CSSStyleValue ?
invalidObject.constructor.name + ' "' + invalidObject.toString() + '"' :
invalidObject;
test(function() {
assert_throws(new TypeError(), function() {
element.attributeStyleMap.set(propertyName, invalidObject);
});
}, 'Setting ' + propertyName + ' to invalid value ' + name + ' throws');
}
}
function runGetterTests(
propertyName, validKeywords, validObjects, validStringMappings, element) {
for (let keyword of validKeywords) {
test(function() {
element.style[propertyName] = keyword;
let result = element.attributeStyleMap.get(propertyName);
assert_equals(result.constructor.name, CSSKeywordValue.name);
assert_equals(result.toString(), keyword);
}, 'Getting ' + propertyName + ' when it is set to ' + keyword);
}
for (let validObject of validObjects) {
test(function() {
element.style[propertyName] = validObject.toString();
let result = element.attributeStyleMap.get(propertyName);
assert_equals(result.constructor.name, validObject.constructor.name,
'typeof result');
assert_equals(result.toString(), validObject.toString());
}, 'Getting ' + propertyName + ' with a ' + validObject.constructor.name +
' whose value is ' + validObject.toString());
}
for (let cssText in validStringMappings) {
test(function() {
element.style[propertyName] = cssText;
let result = element.attributeStyleMap.get(propertyName);
assert_equals(result.constructor.name,
validStringMappings[cssText].constructor.name,
'typeof result');
assert_equals(result.toString(), validStringMappings[cssText].toString());
}, 'Getting ' + propertyName + ' when it is set to "' +
cssText + '" via a string');
}
}
function runSequenceSetterTests(
propertyName, validObject, invalidObject, element, separator) {
test(function() {
element.style = '';
element.attributeStyleMap.set(propertyName, validObject, validObject);
assert_equals(
element.style[propertyName], validObject.toString() + separator +
validObject.toString());
// Force a style recalc to check for crashes in style recalculation.
getComputedStyle(element)[propertyName];
assert_equals(
element.style[propertyName], validObject.toString() + separator +
validObject.toString());
}, 'Set ' + propertyName + ' to a sequence');
test(function() {
assert_throws(new TypeError(), function() {
element.attributeStyleMap.set(propertyName, validObject, invalidObject);
});
}, 'Set ' + propertyName + ' to a sequence containing an invalid type');
}
function runAppendTests(
propertyName, validObject, invalidObject, element, separator) {
test(function() {
element.style = '';
element.attributeStyleMap.append(propertyName, validObject);
assert_equals(element.style[propertyName], validObject.toString());
element.attributeStyleMap.append(propertyName, validObject);
assert_equals(
element.style[propertyName], validObject.toString() + separator +
validObject.toString());
// Force a style recalc to check for crashes in style recalculation.
getComputedStyle(element)[propertyName];
assert_equals(
element.style[propertyName], validObject.toString() + separator +
validObject.toString());
}, 'Appending a ' + validObject.constructor.name + ' to ' + propertyName);
test(function() {
element.style = '';
element.attributeStyleMap.append(propertyName, validObject, validObject);
assert_equals(
element.style[propertyName], validObject.toString() + separator +
validObject.toString());
// Force a style recalc to check for crashes in style recalculation.
getComputedStyle(element)[propertyName];
assert_equals(
element.style[propertyName], validObject.toString() + separator +
validObject.toString());
}, 'Append a sequence to ' + propertyName);
// Negative tests
test(function() {
assert_throws(new TypeError(), function() {
element.attributeStyleMap.append(propertyName, invalidObject);
});
}, 'Appending an invalid value to ' + propertyName);
test(function() {
assert_throws(new TypeError(), function() {
element.attributeStyleMap.append(propertyName, validObject, invalidObject);
});
}, 'Append a sequence containing an invalid value to ' + propertyName);
}
function runGetAllTests(
propertyName, validObject, element, supportsMultiple) {
test(function() {
element.style = '';
assert_array_equals(element.attributeStyleMap.getAll(propertyName), []);
element.style[propertyName] = validObject.toString();
let result = element.attributeStyleMap.getAll(propertyName);
assert_equals(result.length, 1,
'Expected getAll to retrieve an array containing a ' +
'single CSSStyleValue');
assert_equals(result[0].constructor.name, validObject.constructor.name,
'Returned type is incorrect:');
assert_equals(result[0].toString(), validObject.toString());
}, 'getAll for single-valued ' + propertyName);
if (supportsMultiple) {
test(function() {
element.style = '';
element.attributeStyleMap.set(propertyName, validObject, validObject);
let result = element.attributeStyleMap.getAll(propertyName);
assert_equals(result.length, 2,
'Expected getAll to return an array containing two instances ' +
'of ' + validObject.constructor.name);
assert_equals(result[0].constructor.name, validObject.constructor.name);
assert_equals(result[1].constructor.name, validObject.constructor.name);
assert_equals(result[0].toString(), validObject.toString());
assert_equals(result[1].toString(), validObject.toString());
}, 'getAll for list-valued ' + propertyName);
}
}
function runDeletionTests(propertyName, validObject, element) {
test(function() {
element.style[propertyName] = validObject.toString();
assert_not_equals(element.attributeStyleMap.get(propertyName), null);
element.attributeStyleMap.delete(propertyName);
assert_equals(element.style[propertyName], '');
assert_equals(element.attributeStyleMap.get(propertyName), null);
// Force a style recalc to check for crashes in style recalculation.
getComputedStyle(element)[propertyName];
assert_equals(element.style[propertyName], '');
assert_equals(element.attributeStyleMap.get(propertyName), null);
}, 'Delete ' + propertyName + ' removes the value from the styleMap');
}
function runGetPropertiesTests(propertyName, validObject, element) {
test(function() {
element.style = '';
assert_array_equals(element.attributeStyleMap.getProperties(), []);
element.attributeStyleMap.set(propertyName, validObject);
assert_array_equals(element.attributeStyleMap.getProperties(), [propertyName]);
// Force a style recalc to check for crashes in style recalculation.
getComputedStyle(element)[propertyName];
assert_array_equals(element.attributeStyleMap.getProperties(), [propertyName]);
}, propertyName + ' shows up in getProperties');
}
function runMultipleValuesNotSupportedTests(
propertyName, validObject, element) {
test(function() {
element.style = '';
assert_throws(new TypeError(), function() {
element.attributeStyleMap.set(propertyName, validObject, validObject);
});
}, 'Setting ' + propertyName + ' to a sequence throws');
test(function() {
element.style = '';
assert_throws(new TypeError(), function() {
element.attributeStyleMap.append(propertyName, validObject);
});
}, 'Appending to ' + propertyName + ' throws');
}