diff options
Diffstat (limited to 'devtools/shared/tests/xpcshell/test_css-properties-db.js')
-rw-r--r-- | devtools/shared/tests/xpcshell/test_css-properties-db.js | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/devtools/shared/tests/xpcshell/test_css-properties-db.js b/devtools/shared/tests/xpcshell/test_css-properties-db.js new file mode 100644 index 0000000000..78ed8fd70b --- /dev/null +++ b/devtools/shared/tests/xpcshell/test_css-properties-db.js @@ -0,0 +1,180 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Test that the devtool's client-side CSS properties database is in sync with the values + * on the platform (in Nightly only). If they are not, then `mach devtools-css-db` needs + * to be run to make everything up to date. Nightly, aurora, beta, and release may have + * different CSS properties and values. These are based on preferences and compiler flags. + * + * This test broke uplifts as the database needed to be regenerated every uplift. The + * combination of compiler flags and preferences means that it's too difficult to + * statically determine which properties are enabled between Firefox releases. + * + * Because of these difficulties, the database only needs to be up to date with Nightly. + * It is a fallback that is only used if the remote debugging protocol doesn't support + * providing a CSS database, so it's ok if the provided properties don't exactly match + * the inspected target in this particular case. + */ + +"use strict"; + +const { + PSEUDO_ELEMENTS, + CSS_PROPERTIES, +} = require("resource://devtools/shared/css/generated/properties-db.js"); +const PREFERENCES = InspectorUtils.getCSSPropertyPrefs(); +const { + generateCssProperties, +} = require("resource://devtools/server/actors/css-properties.js"); +const { Preferences } = ChromeUtils.importESModule( + "resource://gre/modules/Preferences.sys.mjs" +); + +function run_test() { + const propertiesErrorMessage = + "If this assertion fails, then the client side CSS " + + "properties list in devtools is out of sync with the " + + "CSS properties on the platform. To fix this " + + "assertion run `mach devtools-css-db` to re-generate " + + "the client side properties."; + + // Check that the platform and client match for pseudo elements. + deepEqual( + PSEUDO_ELEMENTS, + InspectorUtils.getCSSPseudoElementNames(), + "The pseudo elements match on the client and platform. " + + propertiesErrorMessage + ); + + /** + * Check that the platform and client match for the details on their CSS properties. + * Enumerate each property to aid in debugging. Sometimes these properties don't + * completely agree due to differences in preferences. Check the currently set + * preference for that property to see if it's enabled. + */ + const platformProperties = generateCssProperties(); + + for (const propertyName in CSS_PROPERTIES) { + const platformProperty = platformProperties[propertyName]; + const clientProperty = CSS_PROPERTIES[propertyName]; + const deepEqual = isJsonDeepEqual(platformProperty, clientProperty); + + if (deepEqual) { + ok(true, `The static database and platform match for "${propertyName}".`); + } else { + ok( + false, + `The static database and platform do not match for ` + + ` + "${propertyName}". ${propertiesErrorMessage}` + ); + } + } + + /** + * Check that the list of properties on the platform and client are the same. If + * they are not, check that there may be preferences that are disabling them on the + * target platform. + */ + const mismatches = getKeyMismatches(platformProperties, CSS_PROPERTIES) + // Filter out OS-specific properties. + .filter(name => name && !name.includes("-moz-osx-")); + + if (mismatches.length === 0) { + ok( + true, + "No client and platform CSS property database mismatches were found." + ); + } + + mismatches.forEach(propertyName => { + if (getPreference(propertyName) === false) { + ok( + true, + `The static database and platform do not agree on the property ` + + `"${propertyName}" This is ok because it is currently disabled through ` + + `a preference.` + ); + } else { + ok( + false, + `The static database and platform do not agree on the property ` + + `"${propertyName}" ${propertiesErrorMessage}` + ); + } + }); +} + +/** + * Check JSON-serializable objects for deep equality. + */ +function isJsonDeepEqual(a, b) { + // Handle primitives. + if (a === b) { + return true; + } + + // Handle arrays. + if (Array.isArray(a) && Array.isArray(b)) { + if (a.length !== b.length) { + return false; + } + for (let i = 0; i < a.length; i++) { + if (!isJsonDeepEqual(a[i], b[i])) { + return false; + } + } + return true; + } + + // Handle objects + if (typeof a === "object" && typeof b === "object") { + for (const key in a) { + if (!isJsonDeepEqual(a[key], b[key])) { + return false; + } + } + + return Object.keys(a).length === Object.keys(b).length; + } + + // Not something handled by these cases, therefore not equal. + return false; +} + +/** + * Take the keys of two objects, and return the ones that don't match. + * + * @param {Object} a + * @param {Object} b + * @return {Array} keys + */ +function getKeyMismatches(a, b) { + const aNames = Object.keys(a); + const bNames = Object.keys(b); + const aMismatches = aNames.filter(key => !bNames.includes(key)); + const bMismatches = bNames.filter(key => { + return !aNames.includes(key) && !aMismatches.includes(key); + }); + + return aMismatches.concat(bMismatches); +} + +/** + * Get the preference value of whether this property is enabled. Returns an empty string + * if no preference exists. + * + * @param {String} propertyName + * @return {Boolean|undefined} + */ +function getPreference(propertyName) { + const preference = PREFERENCES.find(({ name, pref }) => { + return name === propertyName && !!pref; + }); + + if (preference) { + return Preferences.get(preference.pref); + } + return undefined; +} |