Skip to content
This repository has been archived by the owner on Sep 21, 2022. It is now read-only.

Commit

Permalink
Add ability to ignore areas
Browse files Browse the repository at this point in the history
Suite got new method `.ignoreElements`, that allows to set a number
of selectors, which should be ignored during tests.
After screenshot is taken, the ignored elements are filled with black
color on it before comparison.

The format of data, returned from `prepareScreenshot` JS call is changed
so that capture area and igonred area could be processed in a same way.
  • Loading branch information
Sergey Tatarintsev committed Jan 21, 2015
1 parent 6f07a56 commit 11a0bfb
Show file tree
Hide file tree
Showing 15 changed files with 240 additions and 143 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
@@ -1,5 +1,11 @@
# Changelog

## Dev

* Add ability to ignore certain elements when comparing screenshots.
Use `suite.ignoreElements(selector1, selector2, ...)` to specify
the selectors to ignore (@SevInf).

## 0.9.5 - 2014-12-10

* Works on Windows again (@SevInf).
Expand All @@ -25,7 +31,7 @@ a whole testing process (@SevInf).

## 0.9.1 - 2014-10-23

* Ignore ``@keyframes` at-rule while collecting coverage (@scf2k).
* Ignore `@keyframes` at-rule while collecting coverage (@scf2k).

## 0.9.0 - 2014-10-22

Expand Down
3 changes: 3 additions & 0 deletions doc/tests.md
Expand Up @@ -61,6 +61,9 @@ suite.setCaptureElements(['.selector1', '.selector2']);

All tests in a suite will fail if none of the elements will be found.

* `ignoreElements('selector1', 'selector2', ...)` - elements, matching
specified selectors will be ignored when comparing images.

* `skip([browser])` – skip all tests and nested suites for:

- `skip()` – all browsers;
Expand Down
1 change: 1 addition & 0 deletions doc/tests.ru.md
Expand Up @@ -53,6 +53,7 @@
```
**NB**: Все тесты из набора будут неуспешными, если хотя бы один из элементов не будет найден.

* `ignoreElements('selector1', ;)`
* `skip([browser])` – пропустить все тесты и вложенные наборы:
- `skip()` – для всех браузеров;
- `skip(browserName)` или `skip({browserName: browserName})` – для всех версий указанного браузера;
Expand Down
18 changes: 7 additions & 11 deletions lib/browser/client-scripts/gemini.js
Expand Up @@ -32,18 +32,14 @@
}

return {
cropSize: {
width: rect.width,
height: rect.height
},
locationInBody: {
top: rect.top,
left: rect.left
},
locationInViewport: {
top: rect.top - getScrollTop(),
left: rect.left - getScrollLeft()
viewportOffset: {
top: getScrollTop(),
left: getScrollLeft()
},
captureArea: rect,
ignoreAreas: opts.ignoreSelectors.map(function(selector) {
return getScreenshotRect([selector]);
}),
viewportHeight: Math.round(viewportHeight),
bodyHeight: Math.round(bodyHeight),
coverage: coverage,
Expand Down
82 changes: 56 additions & 26 deletions lib/capture-session.js
Expand Up @@ -4,6 +4,7 @@ var util = require('util'),
q = require('q'),
inherit = require('inherit'),
find = require('../lib/find-func').find,
extend = require('node.extend'),
StateError = require('../lib/errors/state-error');

module.exports = inherit({
Expand All @@ -29,50 +30,67 @@ module.exports = inherit({

capture: function(state, opts) {
var _this = this;
opts = extend({}, opts, {
ignoreSelectors: state.ignoreSelectors
});
return _this.runHook(state.callback)
.then(function() {
return _this.browser.prepareScreenshot(state.captureSelectors, opts);
})
.then(function(data) {
.then(function(prepareData) {
return _this.browser.captureFullscreenImage().then(function(image) {
return [image, _this._getCropRect(image, data), data];
_this._validateImage(image, prepareData);
return [
image,
getToImageCoordsFunction(image, prepareData),
prepareData
];
});
})
.spread(function(image, cropRect, data) {
return image.crop(cropRect)
.spread(function(image, toImageCoords, prepareData) {
prepareData.ignoreAreas.forEach(function(area) {
image.clear(toImageCoords(area));
});
return image.crop(toImageCoords(prepareData.captureArea))
.then(function(crop) {
return {
image: crop,
canHaveCaret: data.canHaveCaret,
coverage: data.coverage
canHaveCaret: prepareData.canHaveCaret,
coverage: prepareData.coverage
};
});
});
},

_getCropRect: function(image, pageData) {
_validateImage: function(image, prepareData) {
var imageSize = image.getSize(),
size = pageData.cropSize,
location = pageData.locationInBody;
captureArea = prepareData.captureArea,
bottomBorder = captureArea.top + captureArea.height;

if (imageSize.height < pageData.bodyHeight) {
location = pageData.locationInViewport;
if (imageSize.height < prepareData.bodyHeight) {
bottomBorder -= prepareData.viewportOffset.top;
}

if (location.top + size.height > imageSize.height) {
if (bottomBorder > imageSize.height) {
// This case is handled specially because of Opera 12 browser.
// Problem, described in error message occurs there much more often then
// for other browsers and has different workaround
return q.reject(new StateError(util.format(
throw new StateError(util.format(
'Failed to capture the element because it is positioned outside of the captured body. ' +
'Most probably you are trying to capture an absolute positioned element which does not make body ' +
'height to expand. To fix this place a tall enough <div> on the page to make body expand.\n' +
'Element position: %s, %s; size: %s, %s. Page screenshot size: %s, %s. ',
location.left, location.top, size.width, size.height, imageSize.width, imageSize.height)));
captureArea.left,
captureArea.top,
captureArea.width,
captureArea.height,
imageSize.width,
imageSize.height
));
}

if (isOutsideOfImage(location, size, imageSize)) {
return q.reject(new StateError(
if (isOutsideOfImage(captureArea, imageSize)) {
throw new StateError(
'Can not capture specified region of the page\n' +
'The size of a region is larger then image, captured by browser\n' +
'Check that elements:\n' +
Expand All @@ -81,19 +99,31 @@ module.exports = inherit({
'Alternatively, you can increase browser window size using\n' +
'"setWindowSize" or "windowSize" option in config file.'

));
);
}

return {
top: location.top,
left: location.left,
width: size.width,
height: size.height
};
}

});

function isOutsideOfImage(location, size, imageSize) {
return location.top < 0 || location.left < 0 || location.left + size.width > imageSize.width;
function isOutsideOfImage(area, imageSize) {
return area.top < 0 || area.left < 0 || area.left + area.width > imageSize.width;
}

function getToImageCoordsFunction(image, prepareData) {
var imageSize = image.getSize();
if (imageSize.height >= prepareData.bodyHeight) {
return function toImageCoords(area) {
return area;
};
}

var viewportOffset = prepareData.viewportOffset;
return function toImageCoords(area) {
return {
top: area.top - viewportOffset.top,
left: area.left - viewportOffset.left,
width: area.width,
height: area.height
};
};
}
10 changes: 10 additions & 0 deletions lib/image.js
Expand Up @@ -21,6 +21,16 @@ module.exports = inherit({

save: function save(file) {
return q.ninvoke(this._img, 'save', file);
},

clear: function(area) {
this._img.fill(
area.left,
area.top,
area.width,
area.height,
'#000000'
);
}
}, {
compare: function(path1, path2, options) {
Expand Down
4 changes: 4 additions & 0 deletions lib/state.js
Expand Up @@ -20,6 +20,10 @@ module.exports = inherit({

get captureSelectors() {
return this.suite.captureSelectors;
},

get ignoreSelectors() {
return this.suite.ignoreSelectors;
}

});
1 change: 1 addition & 0 deletions lib/suite.js
Expand Up @@ -23,6 +23,7 @@ var Suite = inherit({
this.url = null;
this.skipped = false;
this.captureSelectors = null;
this.ignoreSelectors = [];
this.beforeHook = function() {};
this.afterHook = function() {};
definePrivate(this);
Expand Down
25 changes: 19 additions & 6 deletions lib/tests-api.js
Expand Up @@ -32,14 +32,17 @@ function notString(arg) {
return typeof arg !== 'string';
}

function argumentsToArray(args) {
if (args.length === 1 && Array.isArray(args[0])) {
return args[0];
} else {
return Array.prototype.slice.call(args);
}
}

function SuiteBuilder(suite) {
this.setCaptureElements = function() {
var selectors;
if (arguments.length === 1 && Array.isArray(arguments[0])) {
selectors = arguments[0];
} else {
selectors = Array.prototype.slice.call(arguments);
}
var selectors = argumentsToArray(arguments);

if (selectors.some(notString)) {
throw new TypeError('suite.captureElements accepts only strings or array of strings');
Expand Down Expand Up @@ -91,6 +94,16 @@ function SuiteBuilder(suite) {
return this;
};

this.ignoreElements = function ignoreElements() {
var selectors = argumentsToArray(arguments);

if (selectors.some(notString)) {
throw new TypeError('suite.ignoreElements accepts only strings or array of strings');
}
suite.ignoreSelectors = selectors;
return this;
};

this.skip = function skip(browser) {
if (!browser) {
suite.skip();
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -10,7 +10,7 @@
"chalk": "~0.4.0",
"coa": "~0.4.0",
"css": "^2.1.0",
"png-img": "~0.3.0",
"png-img": "~0.4.1",
"handlebars": "~1.3.0",
"inherit": "~2.2.1",
"js-yaml": "~3.0.1",
Expand Down

0 comments on commit 11a0bfb

Please sign in to comment.