Статьи

Адаптивные Скриншоты С Каспером

Захватывать скриншоты раздражает, но большую часть времени это нужно делать, и обычно это вы — разработчик — кто должен это делать. Делать несколько скриншотов не так уж и плохо, но, скажем, например, что теперь вы работаете над сайтом, использующим адаптивный веб-дизайн, и вам приходится делать в пять раз больше скриншотов в различных окнах просмотра. Это одно, двухсекундное раздражающее задание теперь начало есть во время вашего обеда.


Сегодня я собираюсь написать быстрый и простой сценарий, чтобы сделать несколько скриншотов любого сайта в различных окнах просмотра и сохранить изображения на диск. Это то, что я впервые увидел на mediaqueri.es и начал внедрять в свой процесс сборки. Он не идеален для реального тестирования, так как он больше похож на проверку работоспособности и дает хороший обзор всего, над чем я, возможно, работаю, участвуя в Responsive Web.

Когда сборка запущена, небольшой скрипт, использующий PhantomJS и CasperJS, может пойти и получить скриншот в различных видовых экранах, которые я определил, и дать мне краткий обзор любой желаемой страницы. Это особенно хорошо, когда вы работаете в большой команде, и у вас может не хватить времени, прежде чем каждая сборка пойдет и проверит каждую страницу и модуль на предмет того, как она выглядит при разных разрешениях. Это также даст вам возможность регулярно показывать клиенту информацию о том, как его сайт изгибается в различных окнах просмотра.

Примечание. PhantomJS — это автономный браузер WebKit, и все, что отрисовывается, будет использовать рендеринг WebKit, так что это не фактическое представление о том, как сайт будет отображаться на различных устройствах, которые могут работать в разных браузерах, и при этом вы можете изменить строку агента пользователя, отправленную в сайт загружен, это не меняет движок рендеринга.

PhantomJS имеет отличный JavaScript API, созданный с учетом тестирования. Для хорошего ознакомления с PhantomJS и использования его для тестирования, здесь есть руководство по nettuts и обязательно ознакомьтесь с официальным сайтом и документацией.

CasperJS — это инструментарий, который находится поверх PhantomJS и облегчает процесс написания скриптов Phantom, предоставляя функции, методы и синтаксический сахар.


Возможно, у вас уже установлен Phantom, особенно если вы уже тестируете свой клиентский код, если нет, то это довольно просто, и вы можете получить более подробные инструкции на официальном сайте.

Для пользователей Windows есть исполняемый файл для загрузки и запуска.

Для пользователей Mac есть и бинарный файл, или вы можете установить его с помощью Homebrew:

1
brew update && brew install phantomjs

Для пользователей Linux есть 64-битный двоичный файл или у вас есть возможность скомпилировать PhantomJS из исходного кода.

После установки вы можете открыть терминал и проверить, что все в порядке, запустив:

1
phantomjs —version

который должен вернуть:

1
1.8.0

После установки Phantom вы также можете установить CasperJS. Для пользователей Mac вы можете снова использовать Homebrew:

1
brew install casperjs

Для пользователей Windows вам нужно добавить свой файл PATH с помощью ";C:\casperjs\batchbin" ( ";C:\casperjs\batchbin" этот путь в зависимости от того, где вы хотите хранить CasperJS). В каталоге batchbin есть пакетный файл с именем casperjs.bat , это будет скрипт, который запускает ваши скрипты Casper без необходимости устанавливать Ruby или Python для его запуска. Всякий раз, когда вам нужно запустить скрипт Casper, просто используйте casperjs.bat scriptname.js а не casperjs scriptname.js .

Затем проверьте, что: casperjs --version возвращает: 1.0.0

Оба эти номера версий являются актуальными на момент написания этой статьи.


Теперь, когда у нас есть оба этих запуска, давайте сделаем пару быстрых Hello Worlds, чтобы убедиться, что и Phantom, и Casper работают как положено.

Создайте новый каталог и создайте в нем два файла JavaScript: hellophantom.js и hellocasper.js . Откройте их в любом редакторе и начните с того, что Phantom действительно работает правильно.

Мы начнем с файла hellophantom.js и напишем быстрый тест, чтобы получить заголовок веб-страницы. Я не буду подробно останавливаться на API PhantomJS, это просто даст вам краткое введение и протестирует нашу установку. Если у вас уже работает PhantomJS, вы можете пропустить эту часть.

Сначала нам нужно установить пару переменных, одна из которых создает экземпляр модуля «веб-страница», а другая — просто как переменная «URL».

1
2
var page = require(‘webpage’).create(),
   url = «http://net.tutsplus.com»;

Затем мы можем создать функцию, которая переходит на веб-страницу, мы передаем URL в качестве аргумента и функцию обратного вызова. Мы получаем статус в нашем обратном вызове (успех или неудача) по open методу.

1
2
page.open(url, function(status) {
});

Теперь мы можем вызвать функцию оценки, чтобы получить заголовок страницы. Мы можем вернуть результат в переменную, назначив ей функцию:

1
2
3
4
5
page.open(url, function(status) {
    var title = page.evaluate(function () {
        return document.title;
    });
});

Наконец, мы просто выйдем из системы, чтобы увидеть результат в терминале и затем выйти из процесса Phantom.

1
2
console.log(‘Hello, World! The Page title on ‘+ url +’ is ‘ + title);
phantom.exit();

Наш готовый скрипт будет выглядеть примерно так.

01
02
03
04
05
06
07
08
09
10
var page = require(‘webpage’).create(),
    url = «http://net.tutsplus.com»;
 
page.open(url, function (status) {
    var title = page.evaluate(function () {
        return document.title;
    });
    console.log(‘Hello, World! The Page title on ‘+ url +’ is ‘ + title);
    phantom.exit();
});

Перейдите в каталог, где находится этот скрипт, и вы можете запустить его с помощью следующей команды:

1
phantomjs hellophantom.js

Через несколько секунд вы получите следующий результат в вашем терминале:

1
Hello, World!

Это здорово, но прежде чем мы продолжим, мы можем просто сделать этот код немного более гибким с быстрым рефакторингом. Для нас доступно несколько модулей, один из которых — системный модуль. Некоторые из свойств системного модуля предоставляют вам доступ к таким вещам, как идентификатор процесса, на котором работает PhantomJS, или используемая ОС, но нас интересует свойство args .

Свойство args возвращает массив аргументов командной строки. Первым элементом в массиве всегда является имя скрипта, но мы можем передать любое количество аргументов из командной строки и использовать их в нашем скрипте. Таким образом, мы можем передать URL, который мы хотим открыть, с фантомом, в командной строке, чтобы мы могли повторно использовать скрипт в любое время без необходимости каждый раз редактировать его.

Нам просто нужно сначала запросить системный модуль, а затем изменить переменную url на аргумент, через который мы проходим:

1
2
system = require(‘system’),
url = system.args[1];

и теперь мы можем запустить скрипт с помощью следующей команды:

1
phantomjs hellophantom.js http://net.tutsplus.com

Теперь, когда мы знаем, что Phantom работает, мы можем перейти к тестированию Каспера. Мы повторим тот же тестовый скрипт, только на этот раз мы будем использовать Casper API.

Сначала нам нужно создать экземпляр casper:

1
var casper = require(«casper»).create();

а затем получить URL-адрес одного из аргументов, переданных с терминала. У Каспера есть собственный синтаксический анализатор командной строки, который располагается поверх того, что поставляется с Phantom, и мы можем получить доступ к любым аргументам, передаваемым из командной строки, как мы делали это ранее. Единственное отличие состоит в том, что наш первый аргумент будет первым, через который мы пройдем, а не именем скрипта (как это было с Phantom)

1
var url = casper.cli.args[0];

API-интерфейс Casper CLI также может принимать именованные параметры, а также позиционные аргументы, мы можем использовать это, если хотим установить некоторые параметры или быть более подробными с нашим сценарием, например:

1
casperjs hellocasper.js argumentOne argumentTwo —option1=this —option2=that

и мы можем получить эти именованные опции, используя cli.get('optionName') , чтобы мы могли сделать что-то вроде следующего, чтобы передать как аргументы, так и опции (если у нас были некоторые параметры конфигурации, которые нужно было установить):

1
2
3
4
var argumentOne = casper.cli.args[0];
var argumentTwo = casper.cli.args[1];
var thisOption = casper.cli.get(‘option’);
var thatOption = casper.cli.get(‘option2’);

Сейчас я просто собираюсь использовать позиционный аргумент для получения URL. Далее мы собираемся запустить метод start() для выполнения любого вида навигации. Метод start принимает строковый URL и функцию обратного вызова.

1
2
3
casper.start(url, function() {
    this.echo(‘Hello, World! The Page title on ‘+ url +’ is ‘);
});

Если вы не хотите использовать всю свою функциональность, вы можете использовать метод then() . Каждый вызов метода then() добавляется в качестве шага в стеке и выполняется линейно, поэтому вместо вышеописанного вы можете получить:

1
2
3
4
casper.start(url);
casper.then(function(){
    this.echo(‘Hello, World! The Page title on ‘+ url +’ is ‘);
});

Я предпочитаю использовать then() , так как мне легче читать, но любой из них приемлем, и на самом деле это всего лишь вопрос вкуса.

Чтобы получить заголовок страницы, нам уже getTitle() метод getTitle() , поэтому мы можем просто использовать его в нашем echo .

1
2
3
4
casper.start(url);
casper.then(function(){
    this.echo(‘Hello, World! The Page title on ‘+ url +’ is ‘ + this.getTitle());
});

Наконец, мы выполняем наши шаги с помощью метода run() , который является обязательным методом, необходимым для запуска вашего скрипта Каспера. Этот метод также может иметь необязательный обратный вызов onComplete для запуска после завершения всех шагов. Если вы использовали обратный вызов, вам необходимо убедиться, что вы выходите из процесса Каспера, используя метод exit() . Вот пример обоих:

01
02
03
04
05
06
07
08
09
10
//this doesn’t need to use the exit method.
casper.run();
 
//OR
 
//this needs the exit method
casper.run(function(){
    this.echo(‘Everything in the stack has ended’);
    this.exit();
})

В качестве альтернативы, вы можете просто связать метод выхода после echo:

1
2
3
casper.run(function(){
    this.echo(‘Everything in the stack has ended’).exit();
})

Опять же, дело вкуса.

Теперь наш полный скрипт HelloCasper.js должен выглядеть так:

01
02
03
04
05
06
07
08
09
10
var casper = require(«casper»).create();
var url = casper.cli.args[0];
 
casper.start(url, function(){
    this.echo(‘Hello, World! The Page title on ‘+ url +’ is ‘ + this.getTitle());
});
 
casper.run(function() {
    this.echo(‘Everything in the stack has ended.’).exit();
});

Теперь мы можем запустить скрипт Каспера с помощью следующей команды:

1
casperjs hellocasper.js http://net.tutsplus.com

Он не делает ничего, отличного от того, что мы уже делали с Phantom, Casper просто дает нам хороший API (с некоторыми дополнительными функциями), чтобы сидеть поверх Phantom, и делает код, который мы пишем, более многословным и читабельным, это особенно полезно при написании сценариев, которые должны перемещаться по сайту.

Теперь давайте погрузимся в сохранение некоторых снимков нашего экрана.


Я собираюсь начать с файла с именем casperscreens.js и создать экземпляр Casper. Затем установите массив, который будет содержать желаемую ширину области просмотра, в которую мы хотим сделать снимки экрана. Каждый элемент в массиве будет состоять из другого массива, который будет иметь ширину и высоту, которые мы хотим установить.

1
2
3
4
5
6
7
8
viewportSizes = [
    [320,480],
    [320,568],
    [600,1024],
    [1024,768],
    [1280,800],
    [1440,900]
]

Я также собираюсь установить переменную для получения URL-адреса из командной строки, а затем хочу запустить регулярное выражение для URL-адреса, чтобы создать каталог для сохранения снимков экрана. Я просто собираюсь удалить http:// разделить и заменить периоды дефисами. Затем мы собираемся запустить casper.start() .

1
2
saveDir = url.replace(/[^a-zA-Z0-9]/gi, ‘-‘).replace(/^https?-+/, »);
casper.start();

Теперь мы собираемся использовать цикл и для каждого размера области просмотра сделать скриншот указанного URL. Мы собираемся установить в окне просмотра размеры, определенные в элементе массива, на котором мы находимся — открыть URL-адрес — подождать 5000 миллисекунд, чтобы убедиться, что страница загружена, — а затем сделать снимки экрана двух типов.

Первый предназначен для определения фактической высоты и ширины, для этого мы используем метод capture() который принимает два аргумента — строку для выходного файла и аргумент объекта для задания того, какую часть страницы обрезать. Второй — для полного скриншота страницы с только определенной шириной, и мы делаем это, используя метод captureSelector() который захватывает область в пределах определенного селектора, в нашем случае мы просто используем body и этот метод принимает два аргумента, первый имя файла, а второе — селектор.

Хотя фактический определенный снимок экрана полезен, я обнаружил, что полезно также иметь полный снимок экрана без хрома, чтобы вы могли видеть, как проходит вся страница.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
casper.each(viewportSizes, function(self, viewportSize, i) {
 
    // set two vars for the viewport height and width as we loop through each item in the viewport array
    var width = viewportSize[0],
        height = viewportSize[1];
 
    //give some time for the page to load
    casper.wait(5000, function() {
 
        //set the viewport to the desired height and width
        this.viewport(width, height);
 
        casper.thenOpen(url, function() {
            this.echo(‘Opening at ‘ + width);
 
            //Set up two vars, one for the fullpage save, one for the actual viewport save
            var FPfilename = saveDir + ‘/fullpage-‘ + width + «.png»;
            var ACfilename = saveDir + ‘/’ + width + ‘-‘ + height + «.png»;
 
            //Capture selector captures the whole body
            this.captureSelector(FPfilename, ‘body’);
 
            //capture snaps a defined selection of the page
            this.capture(ACfilename,{top: 0,left: 0,width: width, height: height});
            this.echo(‘snapshot taken’);
        });
    });
});

Наконец, мы вызываем метод run() и в функции обратного вызова я просто собираюсь повторить, что захват завершен.

1
2
3
casper.run(function() {
    this.echo(‘Finished captures for ‘ + url).exit();
});

Полный скрипт теперь должен выглядеть так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
var casper = require(«casper»).create(),
    viewportSizes = [
    [320,480],
    [320,568],
    [600,1024],
    [1024,768],
    [1280,800],
    [1440,900]
],
    url = casper.cli.args[0],
    saveDir = url.replace(/[^a-zA-Z0-9]/gi, ‘-‘).replace(/^https?-+/, »);
 
casper.start();
 
casper.each(viewportSizes, function(self, viewportSize, i) {
 
    // set two vars for the viewport height and width as we loop through each item in the viewport array
    var width = viewportSize[0],
        height = viewportSize[1];
 
    //give some time for the page to load
    casper.wait(5000, function() {
 
        //set the viewport to the desired height and width
        this.viewport(width, height);
 
        casper.thenOpen(url, function() {
            this.echo(‘Opening at ‘ + width);
 
            //Set up two vars, one for the fullpage save, one for the actual viewport save
            var FPfilename = saveDir + ‘/fullpage-‘ + width + «.png»;
            var ACfilename = saveDir + ‘/’ + width + ‘-‘ + height + «.png»;
 
            //Capture selector captures the whole body
            this.captureSelector(FPfilename, ‘body’);
 
            //capture snaps a defined selection of the page
            this.capture(ACfilename,{top: 0,left: 0,width: width, height: height});
            this.echo(‘snapshot taken’);
        });
    });
});
 
casper.run(function() {
    this.echo(‘Finished captures for ‘ + url).exit();
});

И теперь мы можем запустить этот скрипт с помощью следующей команды:

1
casperjs casperscreens.js http://todomvc.com

Я решил сделать несколько экранов с сайта todomvc.com просто потому, что это адаптивный сайт, который может отображать результаты, которые мы ищем.

Теперь, если вы перейдете к каталогу, из которого был запущен скрипт, вы увидите, что был создан новый каталог, и внутри находятся все ваши PNG.

окончательный

Таким образом, нам удалось написать довольно небольшой кусочек JavaScript, который избавит от многих хлопот, когда в следующий раз босс или клиент захочет сделать несколько скриншотов, а также предоставит дополнительный сценарий, который мы можем добавить к нашему набору инструментов при выполнении некоторого тестирования. , Конечно, это только показывает нам рендеринг WebKit, но для многих этого достаточно.

Теперь попробуйте интегрировать это в процесс сборки, запустите его вместе с другими вашими тестами и используйте функцию захвата экрана, чтобы не только протестировать отзывчивость вашего сайта, но и то, как путешествие пользователей может выглядеть на экранах разных размеров. Кроме того, проверьте плагин Grunt Grunt -Casper, если Grunt является частью вашего процесса сборки.

Если вы являетесь поклонником CoffeeScript, вы даже можете попробовать переписать этот скрипт в синтаксисе CoffeeScript, просто убедитесь, что ваш файл заканчивается расширением .coffee :

1
casperjs casperscreen.coffee http://example.com

И вам даже не нужно беспокоиться о предварительной компиляции ваших скриптов CoffeeScript, Casper.

CasperJS и PhantomJS могут предложить гораздо больше, поэтому проверьте их сайты и посмотрите, как они могут помочь в вашем тестировании.