Статьи

Интересное открытие с CasperJS, jQuery и Transitions

Ранее на этой неделе я посмотрел код, использующий 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), но это казалось проще и помогло.