Skip to content

Commit

Permalink
Merge pull request #37 from humpbackdev/feature/backstop
Browse files Browse the repository at this point in the history
Add backstop.
  • Loading branch information
kporras07 committed Oct 3, 2018
2 parents d6d2742 + 3855dc8 commit ba996aa
Show file tree
Hide file tree
Showing 31 changed files with 36,785 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .ahoy/docker.ahoy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,11 @@ commands:
fi
echo "Lighthouse has been started. It's running on port 8080"
backstop:
usage: Run backstop.
cmd: |
docker run --rm -v $(pwd):/src backstopjs/backstopjs $1
hostfile:
usage: Write entries in host file.
cmd: |
Expand Down
59 changes: 59 additions & 0 deletions backstop.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"id": "backstop_default",
"viewports": [
{
"label": "phone",
"width": 320,
"height": 480
},
{
"label": "tablet",
"width": 1024,
"height": 768
},
{
"label": "laptop",
"width": 1920,
"height": 1080
}
],
"onBeforeScript": "puppet/onBefore.js",
"onReadyScript": "puppet/onReady.js",
"scenarios": [
{
"label": "Google Homepage",
"cookiePath": "backstop_data/engine_scripts/cookies.json",
"url": "https://garris.github.io/BackstopJS/",
"referenceUrl": "https://garris.github.io/BackstopJS/",
"readyEvent": "",
"readySelector": "",
"delay": 0,
"hideSelectors": [],
"removeSelectors": [],
"hoverSelector": "",
"clickSelector": "",
"postInteractionWait": 0,
"selectors": [],
"selectorExpansion": true,
"expect": 0,
"misMatchThreshold" : 0.1,
"requireSameDimensions": true
}
],
"paths": {
"bitmaps_reference": "backstop_data/bitmaps_reference",
"bitmaps_test": "backstop_data/bitmaps_test",
"engine_scripts": "backstop_data/engine_scripts",
"html_report": "backstop_data/html_report",
"ci_report": "backstop_data/ci_report"
},
"report": ["browser"],
"engine": "puppeteer",
"engineOptions": {
"args": ["--no-sandbox"]
},
"asyncCaptureLimit": 5,
"asyncCompareLimit": 50,
"debug": false,
"debugWindow": false
}
Empty file.
Empty file.
25 changes: 25 additions & 0 deletions backstop_data/engine_scripts/casper/clickAndHoverHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
var WAIT_TIMEOUT = 5000;

module.exports = function (casper, scenario) {
var waitFor = require('./waitForHelperHelper')(casper, WAIT_TIMEOUT);
var hoverSelector = scenario.hoverSelector;
var clickSelector = scenario.clickSelector;
var postInteractionWait = scenario.postInteractionWait;

if (hoverSelector) {
waitFor(hoverSelector);
casper.then(function () {
casper.mouse.move(hoverSelector);
});
}

if (clickSelector) {
waitFor(clickSelector);
casper.then(function () {
casper.click(clickSelector);
});
}

// TODO: if postInteractionWait === integer then do ==> wait(postInteractionWait) || elsevvv
waitFor(postInteractionWait);
};
15 changes: 15 additions & 0 deletions backstop_data/engine_scripts/casper/loadCookies.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
var fs = require('fs');

module.exports = function (casper, scenario) {
var cookies = [];
var cookiePath = scenario.cookiePath;

// READ COOKIES FROM FILE IF EXISTS
if (fs.exists(cookiePath)) {
cookies = JSON.parse(fs.read(cookiePath));
}

casper.page.cookies = cookies;
console.log('Cookie state restored with cookies:', JSON.stringify(cookies, null, 2));
casper.userAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36');
};
4 changes: 4 additions & 0 deletions backstop_data/engine_scripts/casper/onBefore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = function (casper, scenario, vp) {
require('./loadCookies')(casper, scenario);
casper.userAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36');
};
5 changes: 5 additions & 0 deletions backstop_data/engine_scripts/casper/onReady.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = function (casper, scenario, vp) {
console.log('SCENARIO> ' + scenario.label);
require('./clickAndHoverHelper')(casper, scenario);
// add more helpers here...
};
18 changes: 18 additions & 0 deletions backstop_data/engine_scripts/casper/waitForHelperHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
var TIMEOUT_DEFAULT = 2000;

module.exports = function (casper, timeout) {
var TIMEOUT = timeout || TIMEOUT_DEFAULT;

return function waitFor (selector) {
if (selector) {
casper.waitForSelector(
selector,
function () {},
function () {
console.error('NOT FOUND > ' + selector);
},
TIMEOUT
);
}
};
};
34 changes: 34 additions & 0 deletions backstop_data/engine_scripts/chromy/clickAndHoverHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
module.exports = function (chromy, scenario) {
var hoverSelector = scenario.hoverSelectors || scenario.hoverSelector;
var clickSelector = scenario.clickSelectors || scenario.clickSelector;
var scrollToSelector = scenario.scrollToSelectors || scenario.scrollToSelector;
var postInteractionWait = scenario.postInteractionWait; // selector [str] | ms [int]

if (hoverSelector) {
chromy
.wait(hoverSelector)
.rect(hoverSelector)
.result(function (rect) {
chromy.mouseMoved(rect.left, rect.top);
});
}

if (clickSelector) {
chromy
.wait(clickSelector)
.click(clickSelector);
}

if (postInteractionWait) {
chromy.wait(postInteractionWait);
}

if (scrollToSelector) {
chromy
.wait(scrollToSelector)
.evaluate(`window._scrollToSelector = '${scrollToSelector}'`)
.evaluate(function () {
document.querySelector(window._scrollToSelector).scrollIntoView();
});
}
};
22 changes: 22 additions & 0 deletions backstop_data/engine_scripts/chromy/loadCookies.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
var fs = require('fs');

module.exports = function (chromy, scenario) {
var cookies = [];
var cookiePath = scenario.cookiePath;

// READ COOKIES FROM FILE IF EXISTS
if (fs.existsSync(cookiePath)) {
cookies = JSON.parse(fs.readFileSync(cookiePath));
}

// MUNGE COOKIE DOMAIN FOR CHROMY USAGE
cookies = cookies.map(cookie => {
cookie.url = 'https://' + cookie.domain;
delete cookie.domain;
return cookie;
});

// SET COOKIES VIA CHROMY
chromy.setCookie(cookies);
console.log('Cookie state restored with:', JSON.stringify(cookies, null, 2));
};
6 changes: 6 additions & 0 deletions backstop_data/engine_scripts/chromy/onBefore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = function (chromy, scenario, vp) {
require('./loadCookies')(chromy, scenario);

// IGNORE ANY CERT WARNINGS
chromy.ignoreCertificateErrors();
};
5 changes: 5 additions & 0 deletions backstop_data/engine_scripts/chromy/onReady.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = function (chromy, scenario, vp) {
console.log('SCENARIO > ' + scenario.label);
require('./clickAndHoverHelper')(chromy, scenario);
// add more ready handlers here...
};
14 changes: 14 additions & 0 deletions backstop_data/engine_scripts/cookies.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"domain": ".www.yourdomain.com",
"path": "/",
"name": "yourCookieName",
"value": "yourCookieValue",
"expirationDate": 1798790400,
"hostOnly": false,
"httpOnly": false,
"secure": false,
"session": false,
"sameSite": "no_restriction"
}
]
Binary file added backstop_data/engine_scripts/imageStub.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions backstop_data/engine_scripts/puppet/clickAndHoverHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module.exports = async (page, scenario) => {
var hoverSelector = scenario.hoverSelectors || scenario.hoverSelector;
var clickSelector = scenario.clickSelectors || scenario.clickSelector;
var scrollToSelector = scenario.scrollToSelector;
var postInteractionWait = scenario.postInteractionWait; // selector [str] | ms [int]

if (hoverSelector) {
for (const hoverSelectorIndex of [].concat(hoverSelector)) {
await page.waitFor(hoverSelectorIndex);
await page.hover(hoverSelectorIndex);
}
}

if (clickSelector) {
for (const clickSelectorIndex of [].concat(clickSelector)) {
await page.waitFor(clickSelectorIndex);
await page.click(clickSelectorIndex);
}
}

if (postInteractionWait) {
await page.waitFor(postInteractionWait);
}

if (scrollToSelector) {
await page.waitFor(scrollToSelector);
await page.evaluate(scrollToSelector => {
document.querySelector(scrollToSelector).scrollIntoView();
}, scrollToSelector);
}
};
65 changes: 65 additions & 0 deletions backstop_data/engine_scripts/puppet/ignoreCSP.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* IGNORE CSP HEADERS
* Listen to all requests. If a request matches scenario.url
* then fetch the request again manually, strip out CSP headers
* and respond to the original request without CSP headers.
* Allows `ignoreHTTPSErrors: true` BUT... requires `debugWindow: true`
*
* see https://github.com/GoogleChrome/puppeteer/issues/1229#issuecomment-380133332
* this is the workaround until Page.setBypassCSP lands... https://github.com/GoogleChrome/puppeteer/pull/2324
*
* @param {REQUEST} request
* @return {VOID}
*
* Use this in an onBefore script E.G.
```
module.exports = async function(page, scenario) {
require('./removeCSP')(page, scenario);
}
```
*
*/

const fetch = require('node-fetch');
const https = require('https');
const agent = new https.Agent({
rejectUnauthorized: false
});

module.exports = async function (page, scenario) {
const intercept = async (request, targetUrl) => {
const requestUrl = request.url();

// FIND TARGET URL REQUEST
if (requestUrl === targetUrl) {
const cookiesList = await page.cookies(requestUrl);
const cookies = cookiesList.map(cookie => `${cookie.name}=${cookie.value}`).join('; ');
const headers = Object.assign(request.headers(), {cookie: cookies});
const options = {
headers: headers,
body: request.postData(),
method: request.method(),
follow: 20,
agent
};

const result = await fetch(requestUrl, options);

const buffer = await result.buffer();
let cleanedHeaders = result.headers._headers || {};
cleanedHeaders['content-security-policy'] = '';
await request.respond({
body: buffer,
headers: cleanedHeaders,
status: result.status
});
} else {
request.continue();
}
};

await page.setRequestInterception(true);
page.on('request', req => {
intercept(req, scenario.url);
});
};
37 changes: 37 additions & 0 deletions backstop_data/engine_scripts/puppet/interceptImages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* INTERCEPT IMAGES
* Listen to all requests. If a request matches IMAGE_URL_RE
* then stub the image with data from IMAGE_STUB_URL
*
* Use this in an onBefore script E.G.
```
module.exports = async function(page, scenario) {
require('./interceptImages')(page, scenario);
}
```
*
*/

const fs = require('fs');
const path = require('path');

const IMAGE_URL_RE = /\.gif|\.jpg|\.png/i;
const IMAGE_STUB_URL = path.resolve(__dirname, '../../imageStub.jpg');
const IMAGE_DATA_BUFFER = fs.readFileSync(IMAGE_STUB_URL);
const HEADERS_STUB = {};

module.exports = async function (page, scenario) {
const intercept = async (request, targetUrl) => {
if (IMAGE_URL_RE.test(request.url())) {
await request.respond({
body: IMAGE_DATA_BUFFER,
headers: HEADERS_STUB,
status: 200
});
} else {
request.continue();
}
};
await page.setRequestInterception(true);
page.on('request', intercept);
};
29 changes: 29 additions & 0 deletions backstop_data/engine_scripts/puppet/loadCookies.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
var fs = require('fs');

module.exports = async (page, scenario) => {
var cookies = [];
var cookiePath = scenario.cookiePath;

// READ COOKIES FROM FILE IF EXISTS
if (fs.existsSync(cookiePath)) {
cookies = JSON.parse(fs.readFileSync(cookiePath));
}

// MUNGE COOKIE DOMAIN
cookies = cookies.map(cookie => {
cookie.url = 'https://' + cookie.domain;
delete cookie.domain;
return cookie;
});

// SET COOKIES
const setCookies = async () => {
return Promise.all(
cookies.map(async (cookie) => {
await page.setCookie(cookie);
})
);
};
await setCookies();
console.log('Cookie state restored with:', JSON.stringify(cookies, null, 2));
};
3 changes: 3 additions & 0 deletions backstop_data/engine_scripts/puppet/onBefore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = async (page, scenario, vp) => {
await require('./loadCookies')(page, scenario);
};

0 comments on commit ba996aa

Please sign in to comment.