diff --git a/src/style/Style.js b/src/style/Style.js
index f8e10280..2e58b283 100644
--- a/src/style/Style.js
+++ b/src/style/Style.js
@@ -162,13 +162,19 @@ var Style = Base.extend(new function() {
// raw value is stored, and conversion only happens in the getter.
fields[set] = function(value) {
var owner = this._owner,
- children = owner && owner._children;
+ children = owner && owner._children,
+ applyToChildren = children && children.length > 0
+ && !(owner instanceof CompoundPath);
// Only unify styles on children of Groups, excluding CompoundPaths.
- if (children && children.length > 0
- && !(owner instanceof CompoundPath)) {
+ if (applyToChildren) {
for (var i = 0, l = children.length; i < l; i++)
children[i]._style[set](value);
- } else if (key in this._defaults) {
+ }
+ // Always store selectedColor in item _values to make sure that
+ // group selected bounds and position color is coherent whether it
+ // has children or not when the value is set.
+ if ((key === 'selectedColor' || !applyToChildren)
+ && key in this._defaults) {
var old = this._values[key];
if (old !== value) {
if (isColor) {
diff --git a/test/helpers.js b/test/helpers.js
index 4ccc68ba..4cd62ca5 100644
--- a/test/helpers.js
+++ b/test/helpers.js
@@ -161,6 +161,72 @@ var compareProperties = function(actual, expected, properties, message, options)
}
};
+/**
+ * Compare 2 image data with resemble.js library.
+ * When comparison fails, expected, actual and compared images are displayed.
+ * @param {ImageData} imageData1 the expected image data
+ * @param {ImageData} imageData2 the actual image data
+ * @param {number} tolerance
+ * @param {string} diffDetail text displayed when comparison fails
+ */
+var compareImageData = function(imageData1, imageData2, tolerance, diffDetail) {
+ /**
+ * Build an image element from a given image data.
+ * @param {ImageData} imageData
+ * @return {HTMLImageElement}
+ */
+ function image(imageData) {
+ var canvas = CanvasProvider.getCanvas(imageData.width, imageData.height);
+ canvas.getContext('2d').putImageData(imageData, 0, 0);
+ var image = new Image();
+ image.src = canvas.toDataURL();
+ CanvasProvider.release(canvas);
+ return image;
+ }
+
+ tolerance = (tolerance || 1e-4) * 100;
+
+ // Use resemble.js to compare image datas.
+ var id = QUnit.config.current.testId,
+ index = QUnit.config.current.assertions.length + 1,
+ result;
+ if (!resemble._setup) {
+ resemble._setup = true;
+ resemble.outputSettings({
+ errorColor: { red: 255, green: 51, blue: 0 },
+ errorType: 'flat',
+ transparency: 1
+ });
+ }
+ resemble(imageData1)
+ .compareTo(imageData2)
+ .ignoreAntialiasing()
+ // When working with imageData, this call is synchronous:
+ .onComplete(function(data) { result = data; });
+ // Compare with tolerance in percentage...
+ var fixed = tolerance < 1 ? ((1 / tolerance) + '').length - 1 : 0,
+ identical = result ? 100 - result.misMatchPercentage : 0,
+ ok = Math.abs(100 - identical) <= tolerance,
+ text = identical.toFixed(fixed) + '% identical',
+ detail = text;
+ if (!ok && diffDetail) {
+ detail += diffDetail;
+ }
+ QUnit.push(ok, text, (100).toFixed(fixed) + '% identical');
+ if (!ok && result && !isNode) {
+ // Get the right entry for this unit test and assertion, and
+ // replace the results with images
+ var entry = document.getElementById('qunit-test-output-' + id)
+ .querySelector('li:nth-child(' + (index) + ')'),
+ bounds = result.diffBounds;
+ entry.querySelector('.test-expected td').appendChild(image(imageData1));
+ entry.querySelector('.test-actual td').appendChild(image(imageData2));
+ entry.querySelector('.test-diff td').innerHTML = '
' + detail
+ + '
'
+ + '';
+ }
+};
+
var comparePixels = function(actual, expected, message, options) {
function rasterize(item, group, resolution) {
var raster = null;
@@ -178,11 +244,6 @@ var comparePixels = function(actual, expected, message, options) {
return raster;
}
- function getImageTag(raster) {
- return '';
- }
-
if (!expected) {
return QUnit.strictEqual(actual, expected, message, options);
} else if (!actual) {
@@ -199,8 +260,8 @@ var comparePixels = function(actual, expected, message, options) {
actualBounds = actual.strokeBounds,
expecedBounds = expected.strokeBounds,
bounds = actualBounds.isEmpty()
- ? expecedBounds
- : expecedBounds.isEmpty()
+ ? expecedBounds
+ : expecedBounds.isEmpty()
? actualBounds
: actualBounds.unite(expecedBounds);
if (bounds.isEmpty()) {
@@ -220,53 +281,15 @@ var comparePixels = function(actual, expected, message, options) {
expectedRaster = rasterize(expected, group, resolution);
if (!actualRaster || !expectedRaster) {
QUnit.push(false, null, null, 'Unable to compare rasterized items: ' +
- (!actualRaster ? 'actual' : 'expected') + ' item is null',
- QUnit.stack(2));
+ (!actualRaster ? 'actual' : 'expected') + ' item is null',
+ QUnit.stack(2));
} else {
- // Use resemble.js to compare the two rasterized items.
- var id = QUnit.config.current.testId,
- index = QUnit.config.current.assertions.length + 1,
- result;
- if (!resemble._setup) {
- resemble._setup = true;
- resemble.outputSettings({
- errorColor: { red: 255, green: 51, blue: 0 },
- errorType: 'flat',
- transparency: 1
- });
- }
- resemble(actualRaster.getImageData())
- .compareTo(expectedRaster.getImageData())
- .ignoreAntialiasing()
- // When working with imageData, this call is synchronous:
- .onComplete(function(data) { result = data; });
- // Compare with tolerance in percentage...
- var tolerance = (options.tolerance || 1e-4) * 100,
- fixed = tolerance < 1 ? ((1 / tolerance) + '').length - 1 : 0,
- identical = result ? 100 - result.misMatchPercentage : 0,
- ok = Math.abs(100 - identical) <= tolerance,
- text = identical.toFixed(fixed) + '% identical',
- detail = text;
- if (!ok &&
- actual instanceof PathItem && expected instanceof PathItem) {
- detail += '\nExpected:\n' + expected.pathData +
- '\nActual:\n' + actual.pathData;
- }
- QUnit.push(ok, text, (100).toFixed(fixed) + '% identical', message);
- if (!ok && result && !isNode) {
- // Get the right entry for this unit test and assertion, and
- // replace the results with images
- var entry = document.getElementById('qunit-test-output-' + id)
- .querySelector('li:nth-child(' + (index) + ')'),
- bounds = result.diffBounds;
- entry.querySelector('.test-expected td').innerHTML =
- getImageTag(expectedRaster);
- entry.querySelector('.test-actual td').innerHTML =
- getImageTag(actualRaster);
- entry.querySelector('.test-diff td').innerHTML = '' + detail
- + '
'
- + '';
- }
+ // Compare the two rasterized items.
+ var detail = actual instanceof PathItem && expected instanceof PathItem
+ ? '\nExpected:\n' + expected.pathData + '\nActual:\n' + actual.pathData
+ : '';
+ compareImageData(actualRaster.getImageData(),
+ expectedRaster.getImageData(), options.tolerance, detail);
}
};
@@ -304,6 +327,36 @@ var compareItem = function(actual, expected, message, options, properties) {
}
};
+/**
+ * Run each callback in a separated canvas context and compare both outputs.
+ * This can be used to do selection drawing tests as it is not possible with
+ * comparePixels() method which relies on the item.rasterize() method which
+ * ignores selection.
+ * @param width the width of the canvas
+ * @param height the height of the canvas
+ * @param expectedCallback the function producing the expected result
+ * @param actualCallback the function producing the actual result
+ */
+var compareCanvas = function(width, height, expectedCallback, actualCallback) {
+ function getImageData(width, height, callback) {
+ var canvas = CanvasProvider.getCanvas(width, height);
+ var project = new Project(canvas);
+ callback();
+ project.view.update();
+ var imageData = canvas.getContext('2d').getImageData(0, 0, width, height);
+ CanvasProvider.release(canvas);
+ project.remove();
+ return imageData;
+ }
+
+ compareImageData(
+ getImageData(width, height, expectedCallback),
+ getImageData(width, height, actualCallback)
+ );
+
+ currentProject.activate();
+};
+
// A list of comparator functions, based on `expected` type. See equals() for
// an explanation of how the type is determined.
var comparators = {
diff --git a/test/tests/Group.js b/test/tests/Group.js
index 3f542204..cc783d4e 100644
--- a/test/tests/Group.js
+++ b/test/tests/Group.js
@@ -134,4 +134,24 @@ test('group.addChildren()', function() {
group.addChildren(children);
equals(group.children.length, 2,
'adding the same item twice should only add it once.');
-})
+});
+
+test('group.setSelectedColor() with selected bound and position', function() {
+ compareCanvas(100, 100,
+ function() {
+ // working: set selected color first then add child
+ var group = new Group();
+ group.bounds.selected = true;
+ group.position.selected = true;
+ group.selectedColor = 'black';
+ group.addChild(new Path.Circle([50, 50], 40));
+ }, function() {
+ // failing: add child first then set selected color
+ var group = new Group();
+ group.bounds.selected = true;
+ group.position.selected = true;
+ group.addChild(new Path.Circle([50, 50], 40));
+ group.selectedColor = 'black';
+ }
+ );
+});