diff --git a/lib/browser/new-browser.js b/lib/browser/new-browser.js index 4c1742150..6d62ee311 100644 --- a/lib/browser/new-browser.js +++ b/lib/browser/new-browser.js @@ -210,7 +210,7 @@ module.exports = class NewBrowser extends Browser { } buildScripts() { - return ClientBridge.build(this, {calibration: this._calibration, coverage: this._coverageEnabled}) + return ClientBridge.build(this, {calibration: this._calibration, coverage: this._coverageEnabled, supportDeprecated: true}) .then((clientBridge) => this._clientBridge = clientBridge); } diff --git a/lib/capture-session.js b/lib/capture-session.js index 4960483ca..318b2abe5 100644 --- a/lib/capture-session.js +++ b/lib/capture-session.js @@ -6,8 +6,7 @@ const debug = require('debug'); const ActionsBuilder = require('./tests-api/actions-builder'); const Browser = require('./browser'); -const {temp, Viewport, errors: {HeightViewportError}} = require('gemini-core'); -const StateError = require('./errors/state-error'); +const {temp, ScreenShooter} = require('gemini-core'); module.exports = class CaptureSession { static create(browser) { @@ -24,6 +23,8 @@ module.exports = class CaptureSession { this.log = debug('gemini:capture:' + this.browser.id); this._postActions = []; + + this._screenShooter = ScreenShooter.create(browser); } runActions(actions) { @@ -37,13 +38,9 @@ module.exports = class CaptureSession { } capture(page) { - return this.browser.captureViewportImage(page) - .then((screenImage) => { - const viewport = page.viewport; - const pixelRatio = page.pixelRatio; - - return this._cropImage(Viewport.create(viewport, screenImage, pixelRatio), page); - }); + return this._screenShooter.capture(page) + .catch((e) => this.extendWithPageScreenshot(e).thenThrow(e)) + .then((image) => ({image, canHaveCaret: page.canHaveCaret})); } runPostActions() { @@ -65,47 +62,4 @@ module.exports = class CaptureSession { browser: this.browser.serialize() }; } - - _cropImage(viewport, page) { - this.log('capture data:', page); - - const captureArea = page.captureArea; - - try { - viewport.validate(captureArea, this.browser); - } catch (e) { - return e instanceof HeightViewportError && this.browser.config.compositeImage - ? this._extendImage(viewport, page) - : this._handleValidateImageError(e, viewport); - } - - viewport.ignoreAreas(page.ignoreAreas); - - return viewport.crop(captureArea) - .then((image) => ({image, canHaveCaret: page.canHaveCaret})); - } - - _handleValidateImageError(e, viewport) { - const path = temp.path({suffix: '.png'}); - const error = new StateError(e.message); - - return viewport.save(path) - .then(() => error.imagePath = path).thenThrow(error); - } - - _extendImage(viewport, page) { - const scrollHeight = Math.min( - viewport.getVerticalOverflow(page.captureArea), - page.viewport.height - ); - - return this.browser - .scrollBy(0, scrollHeight) - .then(() => { - page.viewport.top += scrollHeight; - return this.browser.captureViewportImage(page); - }) - .then((newImage) => viewport.extendBy(scrollHeight, newImage)) - .then(() => this._cropImage(viewport, page)); - } }; diff --git a/package-lock.json b/package-lock.json index 20b8ddc84..bee080697 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2351,9 +2351,9 @@ } }, "gemini-core": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/gemini-core/-/gemini-core-2.8.1.tgz", - "integrity": "sha512-QkJ3VzU76akv9+xHjvIpTANaFVThB9Iyy40azroo8+W+Nih/EdUGJxQJEpvdspv4XWEha6CK21EEdi38QMNy1w==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/gemini-core/-/gemini-core-2.9.0.tgz", + "integrity": "sha512-r7oY+neX0eRiHbgp872hsS7R66h01Z7vBGSAVUR7f4ZU+EPMAF+hzoLdESStXM65K9rG71QJhhm0WvN3kRMp7g==", "requires": { "aliasify": "1.9.0", "bluebird": "3.5.1", @@ -2368,18 +2368,6 @@ "temp": "0.8.3", "uglify-js": "2.8.29", "uglifyify": "3.0.4" - }, - "dependencies": { - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "requires": { - "source-map": "0.5.7", - "uglify-to-browserify": "1.0.2", - "yargs": "3.10.0" - } - } } }, "gemini-coverage": { @@ -10059,6 +10047,16 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "requires": { + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" + } + }, "uglify-to-browserify": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", diff --git a/package.json b/package.json index add1fd270..83bc13f8d 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "debug": "^2.2.0", "fs-extra": "^0.30.0", "gemini-configparser": "^1.0.0", - "gemini-core": "^2.8.1", + "gemini-core": "^2.9.0", "gemini-coverage": "^2.0.0", "graceful-fs": "^4.1.11", "handlebars": "^4.0.5", diff --git a/test/unit/browser/new-browser.js b/test/unit/browser/new-browser.js index 169322b6b..ff6e72163 100644 --- a/test/unit/browser/new-browser.js +++ b/test/unit/browser/new-browser.js @@ -544,7 +544,7 @@ describe('browser/new-browser', () => { calibrator.calibrate.resolves({foo: 'bar'}); return browser.launch(calibrator) - .then(() => assert.calledOnceWith(ClientBridge.build, browser, {calibration: {foo: 'bar'}, coverage: true})); + .then(() => assert.calledOnceWith(ClientBridge.build, browser, {calibration: {foo: 'bar'}, coverage: true, supportDeprecated: true})); }); }); diff --git a/test/unit/capture-session.js b/test/unit/capture-session.js index b3ba57650..15123982b 100644 --- a/test/unit/capture-session.js +++ b/test/unit/capture-session.js @@ -1,25 +1,32 @@ 'use strict'; const Promise = require('bluebird'); -const _ = require('lodash'); const CaptureSession = require('lib/capture-session'); const ActionsBuilder = require('lib/tests-api/actions-builder'); const StateError = require('lib/errors/state-error'); -const {temp, Image, Viewport} = require('gemini-core'); +const {temp, ScreenShooter} = require('gemini-core'); describe('capture session', () => { const sandbox = sinon.sandbox.create(); - let imageStub; beforeEach(() => { - imageStub = sinon.createStubInstance(Image); - sandbox.stub(temp); + + sandbox.spy(ScreenShooter, 'create'); + sandbox.stub(ScreenShooter.prototype, 'capture').resolves(); }); afterEach(() => sandbox.restore()); + describe('constructor', () => { + it('should create a screen shooter instance', () => { + CaptureSession.create({foo: 'bar'}); + + assert.calledOnceWith(ScreenShooter.create, {foo: 'bar'}); + }); + }); + describe('runActions', () => { let browser; let session; @@ -190,196 +197,37 @@ describe('capture session', () => { }); describe('capture', () => { - let page; - let browserStub; - let captureSession; + const capture = (page = {}) => CaptureSession.create({config: {}}).capture(page); beforeEach(() => { - imageStub.crop.returns(Promise.resolve({})); - imageStub.getSize.returns({}); - imageStub.save.returns(Promise.resolve()); - - temp.path.returns('/path/to/img'); - - browserStub = { - config: {}, - captureViewportImage: sinon.stub().returns(Promise.resolve(imageStub)), - scrollBy: sinon.stub().returns(Promise.resolve()) - }; - - page = { - documentWidth: 10, - documentHeight: 10, - captureArea: { - width: 2, - height: 3 - }, - viewport: {top: 1, left: 1, width: 10, height: 10}, - ignoreAreas: [{left: 4, top: 4, width: 1, height: 1}], - canHaveCaret: true - }; - - sandbox.stub(Viewport.prototype, 'crop').returns(Promise.resolve()); - sandbox.stub(Viewport.prototype, 'ignoreAreas'); - - sandbox.spy(Viewport, 'create'); - sandbox.spy(Viewport.prototype, 'extendBy'); - - captureSession = CaptureSession.create(browserStub); - }); - - it('should take screenshot', () => { - return captureSession.capture(_.assign({}, page)) - .then(() => assert.called(browserStub.captureViewportImage)); + sandbox.stub(CaptureSession.prototype, 'extendWithPageScreenshot').returns(Promise.resolve()); }); - it('should create viewport instance', () => { - browserStub.captureViewportImage.withArgs(page).returns(Promise.resolve('image')); + it('should capture image', () => { + ScreenShooter.prototype.capture.withArgs({foo: 'bar'}).resolves('image'); - return captureSession.capture(page) - .then(() => { - assert.calledWith(Viewport.create, page.viewport, 'image', page.pixelRatio); - }); + return capture({foo: 'bar'}) + .then((capture) => assert.equal(capture.image, 'image')); }); - it('should crop image of passed size', () => { - return captureSession - .capture(page) - .then(() => assert.calledWith(Viewport.prototype.crop, page.captureArea)); + it('should return the information about the caret on image', () => { + return capture({canHaveCaret: true}) + .then((capture) => assert.isTrue(capture.canHaveCaret)); }); - it('should return object with cropped image and `canHaveCaret` property', () => { - Viewport.prototype.crop.returns(Promise.resolve('image')); + it('should throw if capture fails', () => { + ScreenShooter.prototype.capture.rejects(new Error('capture fails')); - return assert.eventually.deepEqual(captureSession.capture(page), { - image: 'image', - canHaveCaret: page.canHaveCaret - }); - }); - - it('should clear configured ignore area before cropping image', () => { - return captureSession.capture(page) - .then(() => assert.calledWith(Viewport.prototype.ignoreAreas, page.ignoreAreas)); + return assert.isRejected(capture(), /capture fails/); }); - describe('if validation fails', () => { - const testValidationFail = () => { - it('should not crop image', () => { - return captureSession.capture(page) - .catch(() => assert.notCalled(imageStub.crop)); - }); - - it('should save page screenshot', () => { - return captureSession.capture(page) - .catch(() => assert.calledOnce(imageStub.save)); - }); - - it('should extend error with path to page screenshot', () => { - return captureSession.capture(page) - .catch((error) => assert.equal(error.imagePath, '/path/to/img')); - }); - - it('should return rejected promise', () => { - return assert.isRejected(captureSession.capture(page), StateError); - }); - }; - - describe('with NOT `HeightViewportError`', () => { - beforeEach(() => { - page = {captureArea: {top: -1}}; - }); + it('should extend an error with page screenshot if capture fails', () => { + const err = new Error('capture fails'); - testValidationFail(); - }); - - describe('with `HeightViewportError`', () => { - describe('option `compositeImage` is switched off', () => { - beforeEach(() => { - page = {captureArea: {height: 7}, viewport: {height: 5}}; - }); - - testValidationFail(); - }); - - describe('option `compositeImage` is switched on', () => { - beforeEach(() => { - browserStub.config.compositeImage = true; - }); - - it('should scroll vertically if capture area is higher then viewport', () => { - page = {captureArea: {height: 7}, viewport: {top: 0, height: 5}}; - - return captureSession.capture(page) - .then(() => assert.calledWith(browserStub.scrollBy, 0, 2)); - }); - - it('should scroll vertically until the end of capture area', () => { - page = {captureArea: {height: 11}, viewport: {top: 0, height: 5}}; - - return captureSession.capture(page) - .then(() => { - assert.calledTwice(browserStub.scrollBy); - assert.calledWith(browserStub.scrollBy, 0, 5); - assert.calledWith(browserStub.scrollBy, 0, 1); - }); - }); - - it('should capture scrolled viewport image', () => { - page = {captureArea: {height: 7}, viewport: {top: 0, height: 5}}; - - return captureSession.capture(page) - .then(() => assert.calledWithMatch(browserStub.captureViewportImage, {viewport: {top: 2}})); - }); - - // Test does not fairly check that `captureViewportImage` was called after resolving of `scrollBy` - it('should capture viewport image after scroll', () => { - page = {captureArea: {height: 7}, viewport: {top: 0, height: 5}}; - - const scrolledPage = {captureArea: {height: 7}, viewport: {top: 2, height: 5}}; + ScreenShooter.prototype.capture.rejects(err); - const scroll = browserStub.scrollBy.withArgs(0, 2).named('scroll'); - const captureViewportImage = browserStub.captureViewportImage - .withArgs(scrolledPage).named('captureViewportImage'); - - return captureSession.capture(page) - .then(() => assert.callOrder(scroll, captureViewportImage)); - }); - - it('should extend original image by scrolled viewport image', () => { - page = {captureArea: {height: 7}, viewport: {top: 0, height: 5}}; - - const scrolledPage = {captureArea: {height: 7}, viewport: {top: 2, height: 5}}; - const scrolledViewportScreenshot = imageStub; - - browserStub.captureViewportImage - .withArgs(scrolledPage).returns(Promise.resolve(scrolledViewportScreenshot)); - - return captureSession.capture(page) - .then(() => assert.calledWith(Viewport.prototype.extendBy, 2, scrolledViewportScreenshot)); - }); - - it('should crop capture area which is higher then viewport', () => { - page = {captureArea: {height: 7}, viewport: {top: 0, height: 5}}; - - return captureSession.capture(page) - .then(() => assert.calledWith(Viewport.prototype.crop, page.captureArea)); - }); - - it('should return object with cropped image and `canHaveCaret` property', () => { - Viewport.prototype.crop.returns(Promise.resolve('image')); - - return assert.eventually.deepEqual(captureSession.capture(page), { - image: 'image', - canHaveCaret: page.canHaveCaret - }); - }); - - it('should clear configured ignore area before cropping image', () => { - return captureSession.capture(page) - .then(() => assert.calledWith(Viewport.prototype.ignoreAreas, page.ignoreAreas)); - }); - }); - }); + return capture() + .catch(() => assert.calledOnceWith(CaptureSession.prototype.extendWithPageScreenshot, err)); }); }); });