mirror of
https://github.com/niklasvh/html2canvas.git
synced 2023-08-10 21:13:10 +03:00
Add screenshot collecting server
This commit is contained in:
parent
5dbb197a82
commit
b75fd70042
1
.gitignore
vendored
1
.gitignore
vendored
@ -14,3 +14,4 @@ node_modules/
|
|||||||
npm-debug.log
|
npm-debug.log
|
||||||
debug.log
|
debug.log
|
||||||
tests/reftests.js
|
tests/reftests.js
|
||||||
|
*.log
|
||||||
|
@ -10,10 +10,11 @@ module.exports = function(config) {
|
|||||||
platform: 'Windows 10',
|
platform: 'Windows 10',
|
||||||
version: 'beta'
|
version: 'beta'
|
||||||
},
|
},
|
||||||
sl_stable_firefox: {
|
sl_beta_firefox: {
|
||||||
base: 'SauceLabs',
|
base: 'SauceLabs',
|
||||||
browserName: 'firefox',
|
browserName: 'firefox',
|
||||||
platform: 'Windows 10'
|
platform: 'Windows 8.1',
|
||||||
|
version: 'beta'
|
||||||
},
|
},
|
||||||
sl_ie9: {
|
sl_ie9: {
|
||||||
base: 'SauceLabs',
|
base: 'SauceLabs',
|
||||||
@ -90,16 +91,6 @@ module.exports = function(config) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
|
||||||
'sl_opera_12.15': {
|
|
||||||
base: 'SauceLabs',
|
|
||||||
browserName: 'opera',
|
|
||||||
platform: 'Linux',
|
|
||||||
version: '12.15'
|
|
||||||
},
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
config.set({
|
config.set({
|
||||||
|
|
||||||
// base path that will be used to resolve all patterns (eg. files, exclude)
|
// base path that will be used to resolve all patterns (eg. files, exclude)
|
||||||
@ -173,6 +164,7 @@ module.exports = function(config) {
|
|||||||
'/node_modules': `http://localhost:${port}/base/node_modules`,
|
'/node_modules': `http://localhost:${port}/base/node_modules`,
|
||||||
'/tests': `http://localhost:${port}/base/tests`,
|
'/tests': `http://localhost:${port}/base/tests`,
|
||||||
'/assets': `http://localhost:${port}/base/tests/assets`,
|
'/assets': `http://localhost:${port}/base/tests/assets`,
|
||||||
|
'/screenshot': `http://localhost:8081/screenshot`,
|
||||||
},
|
},
|
||||||
|
|
||||||
client: {
|
client: {
|
||||||
|
@ -28,12 +28,14 @@
|
|||||||
"babel-preset-es2015": "6.24.1",
|
"babel-preset-es2015": "6.24.1",
|
||||||
"babel-preset-flow": "6.23.0",
|
"babel-preset-flow": "6.23.0",
|
||||||
"base64-arraybuffer": "0.1.5",
|
"base64-arraybuffer": "0.1.5",
|
||||||
|
"body-parser": "1.17.2",
|
||||||
"chai": "4.1.1",
|
"chai": "4.1.1",
|
||||||
"chromeless": "^1.2.0",
|
"chromeless": "^1.2.0",
|
||||||
"eslint": "4.2.0",
|
"eslint": "4.2.0",
|
||||||
"eslint-plugin-flowtype": "2.35.0",
|
"eslint-plugin-flowtype": "2.35.0",
|
||||||
"eslint-plugin-prettier": "2.1.2",
|
"eslint-plugin-prettier": "2.1.2",
|
||||||
"express": "4.15.4",
|
"express": "4.15.4",
|
||||||
|
"filenamify-url": "1.0.0",
|
||||||
"flow-bin": "0.50.0",
|
"flow-bin": "0.50.0",
|
||||||
"glob": "7.1.2",
|
"glob": "7.1.2",
|
||||||
"jquery": "3.2.1",
|
"jquery": "3.2.1",
|
||||||
@ -45,6 +47,7 @@
|
|||||||
"karma-mocha": "1.3.0",
|
"karma-mocha": "1.3.0",
|
||||||
"karma-sauce-launcher": "1.1.0",
|
"karma-sauce-launcher": "1.1.0",
|
||||||
"mocha": "3.5.0",
|
"mocha": "3.5.0",
|
||||||
|
"platform": "1.3.4",
|
||||||
"prettier": "1.5.3",
|
"prettier": "1.5.3",
|
||||||
"promise-polyfill": "6.0.2",
|
"promise-polyfill": "6.0.2",
|
||||||
"rimraf": "2.6.1",
|
"rimraf": "2.6.1",
|
||||||
@ -59,7 +62,7 @@
|
|||||||
"flow": "flow",
|
"flow": "flow",
|
||||||
"lint": "eslint src/**",
|
"lint": "eslint src/**",
|
||||||
"test": "npm run flow && npm run lint && npm run karma",
|
"test": "npm run flow && npm run lint && npm run karma",
|
||||||
"karma": "karma start --single-run",
|
"karma": "karma start",
|
||||||
"watch": "webpack --progress --colors --watch"
|
"watch": "webpack --progress --colors --watch"
|
||||||
},
|
},
|
||||||
"homepage": "https://html2canvas.hertzen.com",
|
"homepage": "https://html2canvas.hertzen.com",
|
||||||
|
69
scripts/screenshot-server.js
Normal file
69
scripts/screenshot-server.js
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
const express = require('express');
|
||||||
|
const bodyParser = require('body-parser');
|
||||||
|
const filenamifyUrl = require('filenamify-url');
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
app.use(
|
||||||
|
bodyParser.json({
|
||||||
|
limit: '15mb',
|
||||||
|
type: '*/*'
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const prefix = 'data:image/png;base64,';
|
||||||
|
|
||||||
|
const writeScreenshot = (buffer, body) => {
|
||||||
|
const filename = `${filenamifyUrl(
|
||||||
|
body.test.replace(/^\/tests\/reftests\//, '').replace(/\.html$/, ''),
|
||||||
|
{replacement: '-'}
|
||||||
|
)}!${body.platform.name}-${body.platform.version}.png`;
|
||||||
|
|
||||||
|
fs.writeFileSync(path.resolve(__dirname, '../tests/results/', filename), buffer);
|
||||||
|
};
|
||||||
|
|
||||||
|
app.post('/screenshot', (req, res) => {
|
||||||
|
if (!req.body || !req.body.screenshot) {
|
||||||
|
return res.sendStatus(400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const buffer = new Buffer(req.body.screenshot.substring(prefix.length), 'base64');
|
||||||
|
writeScreenshot(buffer, req.body);
|
||||||
|
return res.sendStatus(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
const chunks = {};
|
||||||
|
|
||||||
|
app.post('/screenshot/chunk', (req, res) => {
|
||||||
|
if (!req.body || !req.body.screenshot) {
|
||||||
|
return res.sendStatus(400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = `${req.body.platform.name}-${req.body.platform.version}-${req.body.test
|
||||||
|
.replace(/^\/tests\/reftests\//, '')
|
||||||
|
.replace(/\.html$/, '')}`;
|
||||||
|
if (!Array.isArray(chunks[key])) {
|
||||||
|
chunks[key] = Array.from(Array(req.body.totalCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
chunks[key][req.body.part] = req.body.screenshot;
|
||||||
|
|
||||||
|
if (chunks[key].every(s => typeof s === 'string')) {
|
||||||
|
const str = chunks[key].reduce((acc, s) => acc + s, '');
|
||||||
|
const buffer = new Buffer(str.substring(prefix.length), 'base64');
|
||||||
|
delete chunks[key];
|
||||||
|
writeScreenshot(buffer, req.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.sendStatus(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use((error, req, res, next) => {
|
||||||
|
console.error(error);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
const listener = app.listen(8081, () => {
|
||||||
|
console.log(listener.address().port);
|
||||||
|
});
|
@ -75,8 +75,15 @@ const testSVG = document => {
|
|||||||
const testForeignObject = document => {
|
const testForeignObject = document => {
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
const canvas = document.createElement('canvas');
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = 1;
|
||||||
|
canvas.height = 1;
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
img.src = `data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><foreignObject><div></div></foreignObject></svg>`;
|
ctx.fillStyle = 'red';
|
||||||
|
ctx.fillRect(0, 0, 1, 1);
|
||||||
|
|
||||||
|
img.src = `data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><foreignObject><img src="${encodeURIComponent(
|
||||||
|
canvas.toDataURL()
|
||||||
|
)}" /></foreignObject></svg>`;
|
||||||
|
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const onload = () => {
|
const onload = () => {
|
||||||
|
13
src/index.js
13
src/index.js
@ -18,6 +18,7 @@ import Color, {TRANSPARENT} from './Color';
|
|||||||
export type Options = {
|
export type Options = {
|
||||||
async: ?boolean,
|
async: ?boolean,
|
||||||
allowTaint: ?boolean,
|
allowTaint: ?boolean,
|
||||||
|
backgroundColor: string,
|
||||||
canvas: ?HTMLCanvasElement,
|
canvas: ?HTMLCanvasElement,
|
||||||
imageTimeout: number,
|
imageTimeout: number,
|
||||||
proxy: ?string,
|
proxy: ?string,
|
||||||
@ -71,14 +72,18 @@ const html2canvas = (element: HTMLElement, config: Options): Promise<*> => {
|
|||||||
const documentBackgroundColor = ownerDocument.documentElement
|
const documentBackgroundColor = ownerDocument.documentElement
|
||||||
? new Color(getComputedStyle(ownerDocument.documentElement).backgroundColor)
|
? new Color(getComputedStyle(ownerDocument.documentElement).backgroundColor)
|
||||||
: TRANSPARENT;
|
: TRANSPARENT;
|
||||||
|
const bodyBackgroundColor = ownerDocument.body
|
||||||
|
? new Color(getComputedStyle(ownerDocument.body).backgroundColor)
|
||||||
|
: TRANSPARENT;
|
||||||
|
|
||||||
const backgroundColor =
|
const backgroundColor =
|
||||||
element === ownerDocument.documentElement
|
element === ownerDocument.documentElement
|
||||||
? documentBackgroundColor.isTransparent()
|
? documentBackgroundColor.isTransparent()
|
||||||
? ownerDocument.body
|
? bodyBackgroundColor.isTransparent()
|
||||||
? new Color(getComputedStyle(ownerDocument.body).backgroundColor)
|
? options.backgroundColor ? new Color(options.backgroundColor) : null
|
||||||
: null
|
: bodyBackgroundColor
|
||||||
: documentBackgroundColor
|
: documentBackgroundColor
|
||||||
: null;
|
: options.backgroundColor ? new Color(options.backgroundColor) : null;
|
||||||
|
|
||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
const result = Feature.SUPPORT_FOREIGNOBJECT_DRAWING.then(
|
const result = Feature.SUPPORT_FOREIGNOBJECT_DRAWING.then(
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
/tests/reftests/background/radial-gradient.html
|
|
||||||
/tests/reftests/text/chinese.html
|
|
||||||
[Edge]/tests/reftests/acid2.html
|
|
||||||
[Edge]/tests/reftests/pseudoelements.html
|
|
||||||
[Edge]/tests/reftests/text/multiple.html
|
|
@ -4,7 +4,12 @@ sauceConnectLauncher(
|
|||||||
{
|
{
|
||||||
username: process.env.SAUCE_USERNAME,
|
username: process.env.SAUCE_USERNAME,
|
||||||
accessKey: process.env.SAUCE_ACCESS_KEY,
|
accessKey: process.env.SAUCE_ACCESS_KEY,
|
||||||
logger: console.log
|
logger: console.log,
|
||||||
|
// Log output from the `sc` process to stdout?
|
||||||
|
verbose: true,
|
||||||
|
|
||||||
|
// Enable verbose debugging (optional)
|
||||||
|
verboseDebugging: true
|
||||||
},
|
},
|
||||||
err => {
|
err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -2,6 +2,8 @@ import {expect} from 'chai';
|
|||||||
import parseRefTest from '../scripts/parse-reftest';
|
import parseRefTest from '../scripts/parse-reftest';
|
||||||
import reftests from './reftests';
|
import reftests from './reftests';
|
||||||
import querystring from 'querystring';
|
import querystring from 'querystring';
|
||||||
|
import platform from 'platform';
|
||||||
|
import Promise from 'promise-polyfill';
|
||||||
|
|
||||||
const DOWNLOAD_REFTESTS = false;
|
const DOWNLOAD_REFTESTS = false;
|
||||||
const query = querystring.parse(location.search.replace(/^\?/, ''));
|
const query = querystring.parse(location.search.replace(/^\?/, ''));
|
||||||
@ -78,7 +80,7 @@ const assertPath = (result, expected, desc) => {
|
|||||||
})
|
})
|
||||||
.forEach(url => {
|
.forEach(url => {
|
||||||
describe(url, function() {
|
describe(url, function() {
|
||||||
this.timeout(30000);
|
this.timeout(60000);
|
||||||
const windowWidth = 800;
|
const windowWidth = 800;
|
||||||
const windowHeight = 600;
|
const windowHeight = 600;
|
||||||
const testContainer = document.createElement('iframe');
|
const testContainer = document.createElement('iframe');
|
||||||
@ -113,7 +115,8 @@ const assertPath = (result, expected, desc) => {
|
|||||||
it('Should render untainted canvas', () => {
|
it('Should render untainted canvas', () => {
|
||||||
return testContainer.contentWindow
|
return testContainer.contentWindow
|
||||||
.html2canvas(testContainer.contentWindow.document.documentElement, {
|
.html2canvas(testContainer.contentWindow.document.documentElement, {
|
||||||
removeContainer: true
|
removeContainer: true,
|
||||||
|
backgroundColor: '#ffffff'
|
||||||
})
|
})
|
||||||
.then(canvas => {
|
.then(canvas => {
|
||||||
try {
|
try {
|
||||||
@ -330,6 +333,80 @@ const assertPath = (result, expected, desc) => {
|
|||||||
result
|
result
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// window.__karma__
|
||||||
|
if (false) {
|
||||||
|
const MAX_CHUNK_SIZE = 75000;
|
||||||
|
|
||||||
|
const sendScreenshot = (tries, body, url) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
|
||||||
|
xhr.onload = () => {
|
||||||
|
if (
|
||||||
|
typeof xhr.status !== 'number' ||
|
||||||
|
xhr.status === 200
|
||||||
|
) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject(
|
||||||
|
`Failed to send screenshot with status ${xhr.status}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// xhr.onerror = reject;
|
||||||
|
|
||||||
|
xhr.open('POST', url, true);
|
||||||
|
xhr.send(body);
|
||||||
|
}).catch(e => {
|
||||||
|
if (tries > 0) {
|
||||||
|
// Older edge browsers and some safari browsers have issues sending large xhr through saucetunnel
|
||||||
|
const data = canvas.toDataURL();
|
||||||
|
const totalCount = Math.ceil(
|
||||||
|
data.length / MAX_CHUNK_SIZE
|
||||||
|
);
|
||||||
|
return Promise.all(
|
||||||
|
Array.apply(
|
||||||
|
null,
|
||||||
|
Array(totalCount)
|
||||||
|
).map((x, part) =>
|
||||||
|
sendScreenshot(
|
||||||
|
0,
|
||||||
|
JSON.stringify({
|
||||||
|
screenshot: data.substr(
|
||||||
|
part * MAX_CHUNK_SIZE,
|
||||||
|
MAX_CHUNK_SIZE
|
||||||
|
),
|
||||||
|
part,
|
||||||
|
totalCount,
|
||||||
|
test: url,
|
||||||
|
platform: {
|
||||||
|
name: platform.name,
|
||||||
|
version: platform.version
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
'/screenshot/chunk'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(e);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return sendScreenshot(
|
||||||
|
1,
|
||||||
|
JSON.stringify({
|
||||||
|
screenshot: canvas.toDataURL(),
|
||||||
|
test: url,
|
||||||
|
platform: {
|
||||||
|
name: platform.name,
|
||||||
|
version: platform.version
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
'/screenshot'
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user