Мар 192016
 

Интернет реклама УБС

Selenium WebDriver: basic авторизация

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

На сегодняшний день количество сайтов использующих basic авторизацию не так уж и велико, чаще это веб-приложения, используемые для административных или корпоративных целей.
Основная проблема при тестировании таких приложений это нативные диалоговые окна, при появлении которых, также как и алертов, дальнейшее управление браузером с помощью Selenium становится просто невозможным.

Deprecated Одним из способов избежать появления диалогового окна может быть 
использование специального URL формата http://username:password@example.com/ 
для передачи учетных данных. Эта конструкция все еще поддерживается некоторыми 
браузерами, однако такое решение является официально устаревшим и иногда может 
отрицательно влиять на корректное выполнение дальнейших запросов на странице. 
Поэтому я настоятельно рекомендую не использовать такой подход для тестирования.

BrowserMob Proxy

При использовании basic аутентификации имя пользователя и пароль включаются в состав веб-запроса в стандартный HTTP заголовок «Authorization». Из этого можно заключить, что для успешного прохождения авторизации достаточно изменить HTTP заголовок перед отправкой на сервер. Сам Selenium не умеет манипулировать отправляемыми запросами, но для этих целей отлично подойдет прокси-сервер, в частности BrowserMob Proxy в силу простоты его подключения. Об интеграции и использовании BrowserMob с Selenium WebDriver можно почитать в предыдущей статье. В случае basic авторизации net.lightbody.bmp.proxy.ProxyServer предоставляет метод для ее автоматического выполнения:

server.autoBasicAuthorization("example.com", "username", "password"); 

Важно то, что первый аргумент autoBasicAuthorization это не URL, а именно доменное имя. Он не должен содержать http:// или других частей URL. Для того, чтобы использовать учетные данные для любого домена нужно оставить первый параметр пустым.

server.autoBasicAuthorization("", "username", "password"); 

Пример автоматической basic-аутентификации с BrowserMob Proxy:

import net.lightbody.bmp.proxy.ProxyServer;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.Proxy;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;

public class ProxyServerBasicAutorizationTest {

    private ProxyServer server;
    private WebDriver driver;

    @Before
    public void startProxy() throws Exception {
        server = new ProxyServer(4444);
        server.start();
        server.autoBasicAuthorization("", "username", "password");
        Proxy proxy = server.seleniumProxy();

        DesiredCapabilities capabilities = new DesiredCapabilities();
        capabilities.setCapability(CapabilityType.PROXY, proxy);

        driver = new FirefoxDriver(capabilities);
    }

    @Test
    public void testAccessProtectedSite() throws Exception {
        driver.get("https://example.com");
        driver.findElement(By.className("sign-out"));
    }

    @After
    public void stopProxyServer() throws Exception {
        driver.quit();
        server.stop();
    }
}

И напоследок ложка дегтя, BrowserMob Proxy работает только с basic-аутентификацией и не предоставляет методов для работы с другими протоколами (Kerberos, NTLM и др.).

AutoIt

Для автоматизации прохождения basic-авторизации можно также использовать AutoIt: нужно перехватить диалоговое окно, ввести учетные данные и подтвердить ввод. Казалось бы ничего сложного, но каждый браузер для авторизации формирует окна разных классов: Firefox использует свои диалоговые окна, IE — класс диалоговых окон Windows, а Chrome и вовсе не создает отдельного окна для ввода. В этом можно убедиться используя утилиту AutoIt Window Info. В единичном случае, например, для Firefox скрипт AutoIt может выглядеть следующим образом:

Local $classForBasicAuthWindow = "[CLASS:MozillaDialogClass]";

;ожидание появления окна 10 секунд
WinWait($classForBasicAuthWindow, "", 10)

If WinExists($classForBasicAuthWindow) Then
	WinActivate($classForBasicAuthWindow)
 
	;имя пользователя
	Send($CmdLine[1] & "{TAB}"); 

	;пароль
	Send($CmdLine[2] & "{ENTER}"); 

EndIf

Файл .au3 необходимо переконвертировать в .exe для дальнейшего использования в ходе выполнения теста. Сделать это можно с помощью Aut2exe:

В случае с авторизацией процесс AutoIt нужно запускать до открытия страницы драйвером, чтобы появившееся окно не заблокировало выполнение теста:

public class AutoItBasicAutorizationTest {

    private final String username = "username";
    private final String password = "password";

    @Test
    public void testBasicAuthenticationFirefox() throws Exception {
        WebDriver driver = new FirefoxDriver();
        File autoIt = new File("src/test/resources/auth.exe");

        // запуск exe с передачей учетных данных
        // в качестве параметра
        Process p = Runtime.getRuntime().exec(
                autoIt.getAbsolutePath() + " " 
                                  + username + " " + password);

        driver.get("https://example.com");
        driver.findElement(By.className("sign-out"));
        driver.quit();
    }
}

Если есть необходимость тестировать во всех браузерах используя один скрипт AutoIt, то его можно расширить требуемыми условиями:

$timeoutSec = 10;
$lookingSec = 0;
While $lookingSec < $timeoutSec
	;Firefox or InternetExplorer or Chrome
    If WinExists("[CLASS:MozillaDialogClass]") _
		Or WinExists("[TITLE:Windows Security; CLASS:#32770]") _
			Or WinExists("[CLASS:Chrome_WidgetWin_1]", _
									"Authentication Required") Then
				;вводим учетные данные
				;имя пользователя
				Send($CmdLine[1] & "{TAB}"); 
				;пароль
				Send($CmdLine[2] & "{ENTER}"); 
				Exit;
    EndIf
    sleep(1000)
	$lookingSec += 1;
WEnd

Приведенные выше заголовки и классы окон могут отличаться в зависимости от версий браузеров и их настроек, проверить и получить Ваши текущие данные можно используя AutoIt Window Info.

Мар 192016
 

Видеозапись выполнения тестов Selenium

Есть ли смысл создавать видеозапись процесса тестирования? Вопрос спорный, но думаю, что каждый из вас хоть раз сталкивался с нестабильно работающими тестами, когда один и тот же тест через раз завершается неудачей без видимых причин. В такой ситуации иногда практически невозможно разобраться в чем конкретно заключалась ошибка.
Записывать выполнение тестов полностью или только особо критические моменты — это решение зависит от Ваших предпочтений и свободного дискового пространства :). Главное, что такая возможность есть и она реализуется относительно несложно.

Для видеозаписи нам понадобится Monte Media Library Screen Recorder. К сожалению, этой библиотеки нет в Maven репозитории, поэтому необходимый нам MonteScreenRecorder.jar нужно скачать на официальном сайте. На момент написания статьи последняя версия рекордера была 0.7.7. Теперь загруженный jar файл можно добавить в Build Path проекта или, если Вы используете Maven, выполнить следующие шаги:

1. Установить библиотеку в локальный репозиторий, из командной строки:

mvn install:install-file -Dfile=MonteScreenRecorder.jar -DgroupId=org.monte 
              -DartifactId=monte-screen-recorder -Dversion=0.7.7 -Dpackaging=jar

-Dfile=абсолютный или относительный путь к файлу

2. Добавить зависимость в pom.xml

org.monte
			monte-screen-recorder
			0.7.7

Сперва создадим собственный класс, который будет вызывать Monte ScreenRecorder и выполнять все необходимые настройки видеозаписи:

import static org.monte.media.FormatKeys.*;
import static org.monte.media.VideoFormatKeys.*;

import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;

import org.monte.media.Format;
import org.monte.media.FormatKeys.MediaType;
import org.monte.media.math.Rational;
import org.monte.screenrecorder.ScreenRecorder;

public class VideoRecorder {

    private ScreenRecorder screenRecorder;

    public void startRecording() {

        try {
            GraphicsConfiguration gc = GraphicsEnvironment
		        .getLocalGraphicsEnvironment().getDefaultScreenDevice()
		        .getDefaultConfiguration();

            this.screenRecorder = new ScreenRecorder(gc, new Format(
		        MediaTypeKey, MediaType.FILE, MimeTypeKey, MIME_AVI),
		        new Format(MediaTypeKey, MediaType.VIDEO, EncodingKey,
			        ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE,
			        CompressorNameKey,
			        ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE, DepthKey,
			        24, FrameRateKey, Rational.valueOf(15), QualityKey,
			        1.0f, KeyFrameIntervalKey, 15 * 60), new Format(
			        MediaTypeKey, MediaType.VIDEO, EncodingKey,
			        "black", FrameRateKey, Rational.valueOf(30)), null);

            this.screenRecorder.start();

        } catch (Exception e) {
            System.out.println(e);
        }
    }

    public void stopRecording() {
        try {
            this.screenRecorder.stop();
        } catch (Exception e) {
            System.out.println(e);
        }
    }

}

Обратите внимание, все действия в методах заключены в блок try … catch для того, чтобы случайно возникшие ошибки видеозаписи не повлияли на дальнейшее выполнение тестов. По умолчанию, ScreenRecorder делает запись всего экрана и сохраняет файл в соответствующую видео директорию ОС ( Windows — Libraries\Videos, Mac — Movies и т.д.) со стандартным именем, например, «ScreenRecording 2013-11-08 at 18.09.00.avi».

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

import static org.monte.media.FormatKeys.*;
import static org.monte.media.VideoFormatKeys.*;

import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.monte.media.Format;
import org.monte.media.FormatKeys.MediaType;
import org.monte.media.math.Rational;
import org.monte.screenrecorder.ScreenRecorder;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.Point;
import org.openqa.selenium.WebDriver;

public class VideoRecorder {

    private final String RECORD_DIRECTORY = "D:\\Projects\\temp\\";

    private ScreenRecorder screenRecorder;

    public void startRecording(WebDriver driver) {

        try {
            GraphicsConfiguration gc = GraphicsEnvironment
		        .getLocalGraphicsEnvironment().getDefaultScreenDevice()
		        .getDefaultConfiguration();

            File dir = new File(RECORD_DIRECTORY);

            // записываем только область окна драйвера
            // для уменьшения размера видео файла
            Point point = driver.manage().window().getPosition();
            Dimension dimension = driver.manage().window().getSize();

            Rectangle rectangle = new Rectangle(point.x, point.y,
		        dimension.width, dimension.height);

            this.screenRecorder = new ScreenRecorder(gc, rectangle, 
		        new Format(MediaTypeKey, MediaType.FILE, MimeTypeKey, 
			        MIME_AVI),
		        new Format(MediaTypeKey, MediaType.VIDEO, EncodingKey,
			        ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE,
			        CompressorNameKey,
			        ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE, DepthKey,
			        24, FrameRateKey, Rational.valueOf(15), QualityKey,
			        1.0f, KeyFrameIntervalKey, 15 * 60), new Format(
			        MediaTypeKey, MediaType.VIDEO, EncodingKey,
			        "black", FrameRateKey, Rational.valueOf(30)), null,
		        dir);

            this.screenRecorder.start();

        } catch (Exception e) {
            System.out.println(e);
        }
    }

    public void stopRecording(String recordName) {

        try {
            this.screenRecorder.stop();

            // переименовываем созданный .avi файл,
            if (recordName != null) {
                SimpleDateFormat dateFormat = new SimpleDateFormat(
			        "yyyy-MM-dd HH.mm.ss");
                File newFileName = new File(String.format("%s%s %s.avi",
			        RECORD_DIRECTORY, recordName,
			        dateFormat.format(new Date())));

                this.screenRecorder.getCreatedMovieFiles().get(0)
			        .renameTo(newFileName);
            }
        } catch (Exception e) {
            System.out.println(e);
        }
    }

}

Пример использования класса VideoRecorder в тесте:

public class RecordedTest {

    private WebDriver driver;

    @Before
    public void setUpDriver() {
        driver = new FirefoxDriver();
    }

    @Test
    public void testRecordFirefox() throws Exception {

        driver.get("http://www.google.com.ua");

        // начинаем запись
        VideoRecorder recorder = new VideoRecorder();
        recorder.startRecording(driver);

        try {
            // критическая секция
            WebElement element = driver.findElement(By.name("q"));
            element.sendKeys("Cheese!");
            element.submit();
        } finally {
            // останавливаем запись
            recorder.stopRecording("Google send Cheese");
        }

    }

    @After
    public void closeDriver() {
        driver.quit();
    }
}

И снова использование блока try … catch, в этом случае его finally секции, для того, чтобы корректно завершить процесс видеозаписи даже в случае возникновения ошибок при выполнении теста.

Так как запись активности осуществляется непосредственно на экране, то такой способ не может быть использован при параллельном запуске тестов, а также с RemoteWebDriver или Grid.

Мар 192016
 

WebDriver и SSL Untrusted certificate

При тестировании веб-приложений, использующих защищенное https соединение, Вы можете столкнуться с ошибками сертификатов при работе с некоторыми веб-драйверами, в частности, с InternetExplorerDriver. Если существует какая-либо проблема с сертификатом, Вы увидите оповещение «Это соединение является недоверенным». Как и в случае с диалоговыми окнами, при появлении такого сообщения работа веб-драйвера с элементами страницы полностью блокируется и продолжать тестирование ресурса дальше просто невозможно.

FirefoxDriver и ChromeDriver

На сегодняшний день FirefoxDriver и ChromeDriver (selenium 2.37.1) автоматически принимают все сертификаты по-умолчанию и не выводят оповещений, поэтому никаких дополнительных настроек при работе с ними не требуется. Однако такое поведение для FirefoxDriver уже отмечено как @Deprecated и возможно в скором времени предупреждения сертификатов безопасности будут обрабатываться иначе.

// Accept untrusted SSL certificates.
@Deprecated
public static final boolean ACCEPT_UNTRUSTED_CERTIFICATES = true;

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

1. Для FirefoxDriver:

DesiredCapabilities capability = DesiredCapabilities.firefox();
capability.setCapability(CapabilityType.ACCEPT_SSL_CERTS, true);
driver = new FirefoxDriver(capability);

или

FirefoxProfile profile = new FirefoxProfile();
profile.setAcceptUntrustedCertificates(true);
driver = new FirefoxDriver(profile);

2. Для ChromeDriver:

ChromeOptions options = new ChromeOptions();
options.addArguments("--ignore-certificate-errors");
driver = new ChromeDriver(options);

В текущей реализации chromedriver.exe опция «—ignore-certificate-errors» передается браузеру по-умолчанию во время инициализации. Для того, чтобы эту функцию отключить можно выполнить следующее (для chromedriver.exe версии не ниже 2.7):

ChromeOptions options = new ChromeOptions();
options.setExperimentalOptions("excludeSwitches",
        Arrays.asList("ignore-certificate-errors"));
driver = new ChromeDriver(options);

Использование ExperimentalOptions является временным решением, потому как в скором времени должна выйти полноценная поддержка excludeSwitches в ChromeOptions.

Internet Explorer

В случае с Internet Explorer вариант с установкой DesiredCapabilities ACCEPT_SSL_CERTS для тестирования не работает. Для IE нельзя временно поменять расширенные параметры, они задаются навсегда. Изменять их приходится вручную еще до начала тестирования. Но я настоятельно рекомендую не понижать настройки безопасности в IE. После смены настроек, факт их изменения обычно забывается и в результате остается машина с неверно сконфигурированным SSL/TSL в IE.

Для того, чтобы продолжить тестирование в IE после вывода такого предупреждения, нам нужно просто кликнуть по ссылке Continue to this website (not recommended), ее id = «overridelink». Но проблема в том, что после появления этого окна работа InternetExplorerDriver частично заблокирована и мы не можем выполнить обычный click() на элементе. Поэтому для нажатия на ссылку можно использовать javascript:

driver.get("javascript:document.getElementById('overridelink').click();");
Мар 192016
 

Установка DKIM в ISPmanager ( Debian, Ubuntu )

Добрый день.

Появилась такая проблема — письма отправленные сервера попадают в спам. По большей части проблема была в некорректной SPF и PTR записях (сделанных не мной). Но я решил дополнить еще и DKIM записью.

Вот краткая заметка, как я это делал.

/usr/local/ispmgr/sbin/pkgctl -D cache
killall -9 ispmgr

1. Заходим в панель управления сервером и переходи на вкладку «Возможности», устанавливаем «OpenDKIM — фильтр DKIM». Если не получается через панель, устанавливаем вручную:
Debian/Ubuntu:

# sudo apt-get install opendkim

Для Debian/Ubuntu необходимо установить дополнительный пакет:

# sudo apt-get install opendkim-tools

Centos/RH:

# yum install opendkim

2. Генерируем этот самый DKIM ключ и сертификат:
Debian/Ubuntu:

# cd /etc/exim4/ssl/
# /usr/bin/opendkim-genkey -D /etc/exim4/ssl -d domain.com -s dkim -r

Centos/RH:

# cd /etc/exim/ssl
# opendkim-genkey -D /etc/exim/ssl -d domain.com -s dkim -r

где domain.com — это ваш домен.

3. Перезагрузим exim:

# sudo /etc/init.d/exim4 restart/

4. Заходим опять в панель управления сервером, на вкладке «Почтовые домены» выбираем нужный домен и жмем кнопку «Изменить» и ставим галочку «Включить DKIM».

Теперь радуемся, что наши письма подписаны и с меньшей вероятностью попадут в спам листы популярных почтовых служб.

Фев 082015
 

Дополнительный ip для интерфейса (ifconfig alias)

Добавляем несколько ip адресов в разный Unix системах.

FreeBSD

Установка адреса алиас через интерфейс:
ifconfig fxp0 alias 192.168.111.110
Удалить существующий алиас с интерфейса:
ifconfig fxp0 -alias 192.168.111.110
Установка алиаса при загрузки системы (rc.conf)
ifconfig_em0_alias0="inet 192.168.111.110"

Debian/Ubuntu

Прописываем алиас на интерфейс (eth0 — оригинальный, eth0:0, eth0:1 — дополнителные)
ifconfig eth0:0 192.168.111.110 up
Установка алиаса при загрузке системы (/etc/network/interfaces)

auto eth0:0
iface eth0:0 inet static
name Ethernet alias LAN card
address 192.168.1.11
netmask 255.255.255.0
broadcast 192.168.1.255
network 192.168.1.0

auto eth0:1
iface eth0:1 inet static
name Ethernet alias LAN card
address 192.168.10.11
netmask 255.255.255.0
broadcast 192.168.10.255
network 192.168.10.0
Удалить существующий алиас с интерфейса:
ifconfig eth0:0 down