Written in the book of the former

This is a Node service that provides a pixel comparison between the design draft and the front end page. It is intended to be an auxiliary test for the testers or the front end personnel themselves. Trust me, at the pixel level, the degree to which a web page has restored the design immediately stands out. If anyone uses this feature to test someone’s page and finds a problem, please don’t say I provided the tool. =

Results the preview

Front knowledge

The following two libraries are used as auxiliary tools:

  • Casperjs: Written based on PhantomJS. Its internal provides a no interface browser, in simple terms, you can use it in the form of code to complete the simulation of people to operate the browser operation, which involves a variety of mouse events, and so on very many functions, this time mainly use its attached screenshot function.
  • Redscreen.js: image pixel comparison tool. The call method is simply understood as passing in two graphs and returning a composite graph with comparison parameters such as degree of difference and so on. The basic implementation idea can be understood as the acquisition of image pixels after the picture is transferred to canvas, and then a comparison is made for each pixel.

Therefore, we should have a big idea of the whole service, which is to enter a website through CasperJS to intercept a page, and then compare it with the design drawing to get the result.

The overall train of thought



From the figure above, we should be able to sort out a general process:

  1. Receive the design draft picture and the website address and node information to be captured from the front page
  2. Save the design to the images folder
  3. Start the sub-process, start casperJS, complete the interception of the target website
  4. Request form.html to fill in the image address information and send it back to the server
  5. The server gets the image information through resemblejs and compares it to the design
  6. The results are passed back to the front-end page

One of the problems that some people might notice is: why did casperJS take a screenshot of the target website instead of sending the information directly back to the server, it chose to open a form page and submit the information in form form?

A: First of all, I don’t know much about CasperJS and Node. What I understand is that casperJS is not a node module, it runs in the operating system. I haven’t found how to establish communication with node service in CasperJS. Because I really don’t know much about Casper! Secondly, due to the inability to establish communication, I had to settle for the next best thing. I quickly opened a form page I had written through Casper and filled in the picture information to send back to the server, which could fulfill the initial appeal. So there’s the from.html section above.

Implementation details

Implement a simple static server

Because it involves the return of index. HTML and form. HTML pages, you need to implement a super simple static server. The code is as follows:

const MIME_TYPE = { "css": "text/css", "gif": "image/gif", "html": "text/html", "ico": "image/x-icon", "jpeg": "image/jpeg", "jpg": "image/jpg", "js": "text/javascript", "json": "application/json", "pdf": "application/pdf", "png": "image/png", "svg": "image/svg+xml", "swf": "application/x-shockwave-flash", "tiff": "image/tiff", "txt": "text/plain", "wav": "audio/x-wav", "wma": "audio/x-ms-wma", "wmv": "video/x-ms-wmv", "xml": "text/xml" } function sendFile(filePath, res) { fs.open(filePath, 'r+', Function (err){if(err){send404(res)}else{let ext = path. extName (filePath) ext = ext? ext.slice(1) : 'unknown' let contentType = MIME_TYPE (ext) | | "text/plain" matching file type / / fs. ReadFile (filePath, function (err, data) {if (err) { send500(res) }else{ res.writeHead(200,{'content-type':contentType}) res.end(data) } }) } }) }Copy the code

Parse the form and store the images in the Images folder

Const multiparty = require('multiparty') // Let form = new multiparty.form () form.parse(req, function (err, fields, files) { let filename = files['file'][0].originalFilename, targetPath = __dirname + '/images/' + filename, if(filename){ fs.createReadStream(files['file'][0].path).pipe(fs.createWriteStream(targetPath)) ... }})Copy the code

Create a readable stream to read the file and pipe it to the specified path to save the image.

Run casperjs

const { spawn } = require('child_process')
spawn('casperjs', ['casper.js', filename, captureUrl, selector, id])
casperjs.stdout.on('data', (data) => {
    ...
}) 
Copy the code

Spawn allows you to create child processes to start casperJS, as well as exec and so on.

Take a screenshot and submit the data to form.html

Const system = require('system') const host = 'http://10.2.45.110:3033' const casper = require('casper').create({// ViewportSize: {width: 1920, height: 4080 } }) const fileName = decodeURIComponent(system.args[4]) const url = decodeURIComponent(system.args[5]) const selector = decodeURIComponent(system.args[6]) const id = decodeURIComponent(system.args[7]) const time = new Date().getTime() casper.start(url) casper.then(function() {console.log(' Screenshot later ') this.captureSelector('./images/casper'+ id + time +'.png', selector) }) casper.then(function() { casper.start(host + '/form.html', function() { this.fill('form#contact-form', { 'diff': './images/casper'+ id + time +'.png', 'point': './images/' + fileName, 'id': id }, true) }) }) casper.run()Copy the code

The code is fairly simple. The main process is to open a page, pass in your actions in then, and run. During this process, I didn’t quite know how to communicate with the Node service, so I chose to open another page. Want to further study can go to see casperJS official website is very detailed!

Eplay.js is used to compare pixels and return data

function complete(data) { let imgName = 'diff'+ new Date().getTime() +'.png', imgUrl, analysisTime = data.analysisTime, misMatchPercentage = data.misMatchPercentage, resultUrl = './images/' + imgName fs.writeFileSync(resultUrl, data.getBuffer()) imgObj = { ... } let resEnd = resObj[id] {'Content-type':'application/json'}) resEnd.end(JSON.stringify(imgObj)) } let result = resemble(diff).compareTo(point).ignoreColors().onComplete(complete)Copy the code

There is a point where the result I get now is returned to the original request, and I have been transferred many times from the original request, so I can’t find my original return body RES. After thinking for a long time, we can only temporarily use the set global object. After receiving the initial request, the IP and timestamp of the requester are set as the unique ID of the object, and the value is the current RES. At the same time, the id is passed at all times in the whole transfer process. Finally, resObj[ID] is called to get the initial return body and return data. This method I don’t think is the optimal solution, but since I can’t think of a good way to run the entire service has to. Please let me know if you have any new ideas!!

The deployment of

Install PhantomJS (OSX)

Phantomjs-2.1.1-macosx. zip Zip: /User/ XXX /phantomjs-2.1.1-macosx Add environment variables: ~ /. In the following file to add the export PATH = "$PATH: / Users/XXX/phantomjs - 2.1.1 - macosx/bin" terminal input: Phantomjs --version If the version number is displayed, the installation is successfulCopy the code

Install casperjs

brew update && brew install casperjs
Copy the code

Install resemble. Js

Brew install pkg-config Cairo libpng jpeg giflib CNPM I canvas // run canvas inside the nodeCopy the code

The node service

git clone https://github.com/Aaaaaaaty/gui-auto-test.git cd gui-auto-test cnpm i cd pxdiff nodemon server.js Open http://localhost:3033/index.htmlCopy the code

reference

  • PhantomJS installation
  • Casperjs document
  • Resemble the js document

The last

The blog of Po author is updated from time to time – if you have any questions, please feel free to share them under issues.