Capture screenshots while running karma tests

This commit is contained in:
Niklas von Hertzen 2017-08-30 21:31:51 +08:00
parent a41ba8852f
commit 23b6f29ecf
8 changed files with 71 additions and 23 deletions

View File

@ -163,8 +163,7 @@ module.exports = function(config) {
'/dist': `http://localhost:${port}/base/dist`, '/dist': `http://localhost:${port}/base/dist`,
'/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: {

View File

@ -1,10 +1,27 @@
const Server = require('karma').Server;
const cfg = require('karma').config;
const path = require('path'); const path = require('path');
const karmaConfig = cfg.parseConfig(path.resolve('./karma.conf.js'));
const server = new Server(karmaConfig, (exitCode) => {
console.log('Karma has exited with ' + exitCode);
process.exit(exitCode)
});
const fs = require('fs'); const fs = require('fs');
const express = require('express'); const express = require('express');
const bodyParser = require('body-parser'); const bodyParser = require('body-parser');
const cors = require('cors');
const filenamifyUrl = require('filenamify-url'); const filenamifyUrl = require('filenamify-url');
const app = express(); const app = express();
app.use(cors());
app.use(function(req, res, next) {
// IE9 doesn't set headers for cross-domain ajax requests
if(typeof(req.headers['content-type']) === 'undefined'){
req.headers['content-type'] = "application/json";
}
next();
});
app.use( app.use(
bodyParser.json({ bodyParser.json({
limit: '15mb', limit: '15mb',
@ -20,7 +37,7 @@ const writeScreenshot = (buffer, body) => {
{replacement: '-'} {replacement: '-'}
)}!${body.platform.name}-${body.platform.version}.png`; )}!${body.platform.name}-${body.platform.version}.png`;
fs.writeFileSync(path.resolve(__dirname, '../tests/results/', filename), buffer); fs.writeFileSync(path.resolve(__dirname, './tests/results/', filename), buffer);
}; };
app.post('/screenshot', (req, res) => { app.post('/screenshot', (req, res) => {
@ -65,5 +82,7 @@ app.use((error, req, res, next) => {
}); });
const listener = app.listen(8081, () => { const listener = app.listen(8081, () => {
console.log(listener.address().port); server.start();
}); });

View File

@ -31,6 +31,7 @@
"body-parser": "1.17.2", "body-parser": "1.17.2",
"chai": "4.1.1", "chai": "4.1.1",
"chromeless": "^1.2.0", "chromeless": "^1.2.0",
"cors": "2.8.4",
"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",
@ -62,7 +63,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", "karma": "node karma",
"watch": "webpack --progress --colors --watch" "watch": "webpack --progress --colors --watch"
}, },
"homepage": "https://html2canvas.hertzen.com", "homepage": "https://html2canvas.hertzen.com",

View File

@ -37,7 +37,7 @@ export class DocumentCloner {
if (backgroundImage.method === 'url') { if (backgroundImage.method === 'url') {
return this.imageLoader return this.imageLoader
.inlineImage(backgroundImage.args[0]) .inlineImage(backgroundImage.args[0])
.then(src => (src ? `url("${src}")` : 'none')); .then(img => (img ? `url("${img.src}")` : 'none'));
} }
return Promise.resolve( return Promise.resolve(
`${backgroundImage.prefix}${backgroundImage.method}(${backgroundImage.args.join( `${backgroundImage.prefix}${backgroundImage.method}(${backgroundImage.args.join(
@ -54,9 +54,12 @@ export class DocumentCloner {
}); });
if (node instanceof HTMLImageElement) { if (node instanceof HTMLImageElement) {
this.imageLoader.inlineImage(node.src).then(src => { this.imageLoader.inlineImage(node.src).then(img => {
if (src && node instanceof HTMLImageElement) { if (img && node instanceof HTMLImageElement && node.parentNode) {
node.src = src; node.parentNode.replaceChild(
copyCSSStyles(node.style, img.cloneNode(false)),
node
);
} }
}); });
} }

View File

@ -50,9 +50,9 @@ export default class ImageLoader<T> {
} }
} }
inlineImage(src: string): Promise<string> { inlineImage(src: string): Promise<Image> {
if (isInlineImage(src)) { if (isInlineImage(src)) {
return Promise.resolve(src); return loadImage(src, this.options.imageTimeout || 0);
} }
if (this.hasImageInCache(src)) { if (this.hasImageInCache(src)) {
return this.cache[src]; return this.cache[src];
@ -61,7 +61,7 @@ export default class ImageLoader<T> {
return this.xhrImage(src); return this.xhrImage(src);
} }
xhrImage(src: string): Promise<string> { xhrImage(src: string): Promise<Image> {
this.cache[src] = new Promise((resolve, reject) => { this.cache[src] = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => { xhr.onreadystatechange = () => {
@ -87,7 +87,7 @@ export default class ImageLoader<T> {
} }
xhr.open('GET', src, true); xhr.open('GET', src, true);
xhr.send(); xhr.send();
}); }).then(src => loadImage(src, this.options.imageTimeout || 0));
return this.cache[src]; return this.cache[src];
} }
@ -196,3 +196,24 @@ const isInlineBase64Image = (src: string): boolean => INLINE_BASE64.test(src);
const isSVG = (src: string): boolean => const isSVG = (src: string): boolean =>
src.substr(-3).toLowerCase() === 'svg' || INLINE_SVG.test(src); src.substr(-3).toLowerCase() === 'svg' || INLINE_SVG.test(src);
const loadImage = (src: string, timeout: number) => {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = reject;
img.src = src;
if (img.complete === true) {
// Inline XML images may fail to parse, throwing an Error later on
setTimeout(() => {
resolve(img);
}, 500);
}
if (timeout) {
setTimeout(
() => reject(__DEV__ ? `Timed out (${timeout}ms) loading image` : ''),
timeout
);
}
});
};

View File

@ -3,7 +3,7 @@
export const contains = (bit: number, value: number): boolean => (bit & value) !== 0; export const contains = (bit: number, value: number): boolean => (bit & value) !== 0;
export const copyCSSStyles = (style: CSSStyleDeclaration, target: HTMLElement): void => { export const copyCSSStyles = (style: CSSStyleDeclaration, target: HTMLElement): HTMLElement => {
// Edge does not provide value for cssText // Edge does not provide value for cssText
for (let i = style.length - 1; i >= 0; i--) { for (let i = style.length - 1; i >= 0; i--) {
const property = style.item(i); const property = style.item(i);
@ -12,6 +12,7 @@ export const copyCSSStyles = (style: CSSStyleDeclaration, target: HTMLElement):
target.style.setProperty(property, style.getPropertyValue(property)); target.style.setProperty(property, style.getPropertyValue(property));
} }
} }
return target;
}; };
export const SMALL_IMAGE = export const SMALL_IMAGE =

View File

@ -32,11 +32,12 @@ export type Options = {
offsetY: number offsetY: number
}; };
const html2canvas = (element: HTMLElement, config: Options = {}): Promise<*> => { const html2canvas = (element: HTMLElement, conf: ?Options): Promise<*> => {
if (typeof console === 'object' && typeof console.log === 'function') { if (typeof console === 'object' && typeof console.log === 'function') {
console.log(`html2canvas ${__VERSION__}`); console.log(`html2canvas ${__VERSION__}`);
} }
const config = conf || {};
const logger = new Logger(); const logger = new Logger();
const ownerDocument = element.ownerDocument; const ownerDocument = element.ownerDocument;

View File

@ -81,6 +81,7 @@ const assertPath = (result, expected, desc) => {
.forEach(url => { .forEach(url => {
describe(url, function() { describe(url, function() {
this.timeout(60000); this.timeout(60000);
this.retries(2);
const windowWidth = 800; const windowWidth = 800;
const windowHeight = 600; const windowHeight = 600;
const testContainer = document.createElement('iframe'); const testContainer = document.createElement('iframe');
@ -334,13 +335,15 @@ const assertPath = (result, expected, desc) => {
); );
} }
// window.__karma__ if (window.__karma__) {
if (false) {
const MAX_CHUNK_SIZE = 75000; const MAX_CHUNK_SIZE = 75000;
const sendScreenshot = (tries, body, url) => { const sendScreenshot = (tries, body, server) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest(); const xhr =
'withCredentials' in new XMLHttpRequest()
? new XMLHttpRequest()
: new XDomainRequest();
xhr.onload = () => { xhr.onload = () => {
if ( if (
@ -354,9 +357,9 @@ const assertPath = (result, expected, desc) => {
); );
} }
}; };
// xhr.onerror = reject; xhr.onerror = reject;
xhr.open('POST', url, true); xhr.open('POST', server, true);
xhr.send(body); xhr.send(body);
}).catch(e => { }).catch(e => {
if (tries > 0) { if (tries > 0) {
@ -385,7 +388,7 @@ const assertPath = (result, expected, desc) => {
version: platform.version version: platform.version
} }
}), }),
'/screenshot/chunk' 'http://localhost:8081/screenshot/chunk'
) )
) )
); );
@ -404,7 +407,7 @@ const assertPath = (result, expected, desc) => {
version: platform.version version: platform.version
} }
}), }),
'/screenshot' 'http://localhost:8081/screenshot'
); );
} }
}); });