Ранее на этой неделе я посмотрел код, использующий CasperJS . CasperJS — это утилита для тестирования PhantomJS , автономного (то есть виртуального) браузера Webkit. Это, вероятно, несправедливо, но мне нравится думать о Каспере как о сверхмощном Керле. Надеюсь, вы знаете Curl как инструмент командной строки, который позволяет вам выполнять сетевые запросы и работать с результатом. В отличие от Curl, CasperJS (и PhantomJS) могут взаимодействовать с результатами, как настоящий браузер. Это позволяет провести несколько классных тестов / утилит. Я только начал царапать поверхность инструмента, но подумал, что поделюсь интересной небольшой проблемой, с которой столкнулся мой коллега.
Мой коллега, Пол Робертсон (умный и дружелюбный парень, которому нужно снова запустить свой блог), создал скрипт CasperJS, который сканировал набор URL-адресов и загружал HTML-код в локальный каталог. Это само по себе звучит так же, как Curl. Но эти страницы были особенными. Они использовали jQuery для выполнения XHR-запроса к JSON-файлу. Как только запрос XHR был выполнен, данные были преобразованы в HTML и преобразованы в DOM. Здесь светит CasperJS / PhantomJS. Он смог сказать браузеру без головы дождаться появления определенного селектора (тот, который используется в коде JavaScript для рендеринга HTML) и только потом фактически сохранить результат. Круто, верно?
Чтобы дать вам представление, вот пример файла. Этот загружается в простой фрагмент HTML, но вы поняли идею.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
</head>
<body>
<div id="main"></div>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script>
$(document).ready(function() {
var $main = $("#main");
//fetch the content and insert it...
//timeout here just to fake some network latency
window.setTimeout(function() {
$.get("data.html", function(res) {
$main.append(res);
});
}, 800);
});
</script>
</body>
</html>
Ничего странного там нет, так что я не буду это обсуждать. Теперь давайте посмотрим на скрипт CasperJS. Это упрощенная версия того, что написал Павел. Даже если вы впервые в жизни видите CasperJS в действии, вы, вероятно, можете понять, что происходит.
var casper = require("casper").create();
var fs = require("fs");
var outputFolder = "./output/";
var timeoutLength = 2000;
// TODO: delete contents of output folder
if(fs.exists(outputFolder)) {
console.log("Removing "+outputFolder);
fs.removeTree(outputFolder);
}
casper.start();
var url = "http://localhost/testingzone/trash/testtransition2.html";
var selector = "#dynamicContent";
console.log("Getting "+url);
casper.thenOpen(url);
casper.waitForSelector(selector, function then() {
var htmlToWrite = this.getHTML();
var outputFile = outputFolder + "testoutput.html";
fs.write(outputFile, htmlToWrite, "w");
console.log("Wrote html to " + outputFile);
}, function timeout() {
this.echo("Selector not found after " + timeoutLength + " ms");
}, timeoutLength);
casper.run();
В результате получается HTML-файл с содержимым, в том числе загруженным через jQuery. Просто для полноты, вот фрагмент HTML. Обратите внимание, что у него есть селектор, который ждет мой скрипт.
<div id="dynamicContent"> This is so cool. </div>
Woot! Итак, как я уже сказал — что это на самом деле чертовски круто. Мы выдвинули контент для тестирования, и кто-то указал на что-то странное. Область, в которую загружался динамический контент, имела странный серый оттенок. Если вам интересно, это второй результат Google для «нечетного серого затемнения»:

Я немного покопался и обнаружил что-то действительно странное. У div, в который было вставлено динамическое содержимое, было следующее:
style="display: block; opacity: 0.04429836168227741; "
WTF
Я сделал еще несколько нажатий — на этот раз на оригинальной версии — и затем я увидел это. Когда динамический контент был загружен, jQuery выполнял какое-то необычное действие постепенного появления / исчезновения. Потому что … модно. Поэтому я немного изменил свой оригинальный код, чтобы попытаться воссоздать это:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
</head>
<body>
<div id="main">Existing HTML...</div>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script>
$(document).ready(function() {
var $main = $("#main");
//fetch the content and insert it...
//timeout here just to fake some network latency
window.setTimeout(function() {
$main.fadeOut(function() {
$.get("data.html", function(res) {
$main.html(res);
$main.fadeIn();
})
})
}, 20);
});
</script>
</body>
</html>
Обратите внимание на использование причудливого угасающего действия. Необычные! Затем я перезапустил свой скрипт CasperJS и сразу увидел, что то же самое было добавлено в мой вывод. Это было исчезать! CasperJS увидел элемент DOM, потому что, ну, он был там, но jQuery не закончил его возвращение. Я обнаружил, что у CasperJS была простая команда ожидания, поэтому я добавил это:
casper.waitForSelector(selector, function then() {
this.wait(1000);
var htmlToWrite = this.getHTML();
var outputFile = outputFolder + "testoutput.html";
fs.write(outputFile, htmlToWrite, "w");
console.log("Wrote html to " + outputFile);
}, function timeout() {
this.echo("Selector not found after " + timeoutLength + " ms");
}, timeoutLength);
И это сделало это. Технически я мог бы использовать меньшее ожидание (длительность перехода jQuery по умолчанию равна 400), но это казалось проще и помогло.