четверг, 10 ноября 2011 г.

LogonDialogHandler VS Windows 7 (Решение в Watin)

Заметки юного автоматизатора... Так уж получилось, что свои тесты я синхронизирую между тремя компами - рабочий ноутбук (ХР), домашний ноутбук (Vista) и домашний комп (Win7). Компьютер у меня очень "вовремя" полетел, так что поставила студию и на домашний ноут. Проблем с двумя ноутбуками не было никаких - все хорошо, все круто, все работает. А потом мне вернули комп... У вас тоже Windows 7? И вы хотите начать автоматизацию? Тогда честно вам скажу, семерка - зло!

Нет. Не так.

Windows 7 - ЗЛО!!! Особенно для начинающих автоматизаторов, которые в код то круглыми глазами смотрят, "ой, а что это?", а тут еще идеально работающий код валится на первой же стадии.

Да да, это страшное слово "Залогиниться". При моих попытках освоить TestComlete я, кстати, семерку так и не победила. Неуверенно тыкаясь в кнопочку рекорда и осторожно корректируя полученный скрипт до понятного, я была страшно огорчена тем, что прекрасно работающие тесты на ХР не запускаются дома. Вообще. Ведь для любого теста надо вначале залогиниться... А он не видит найденный ранее Alias. Пыталась перезаписать скрипт, найти окно логина семерки. Но проиграла этот раунд)))

Потом у меня кончилась триал-версия, так что больше я TestComlete не занималась, и как обойти окно логина там, я не знаю.

Едем дальше. Watin, VS 2010, C#. Все хорошо, все здорово. Запись логина:

browser.DialogWatcher.Add(new LogonDialogHandler(@"login", "password"));

На семерке запускаю - вводит логин и пароль в строку логина, причем упорно так вводит, раз 20 ввел, пока скрипт не остановила. Пошла за помощью к программистам. Посидели, погуглили. Нашли обход.

Делаем так. В панели инструментов у нас есть такая штучка, как Debug, если нет, вытаскиваем ее туда:




Открываем выпадающий список - Configuration Manager. А теперь угадайте, где в этом менеждере кнопка "добавить новую конфигурацию" )))))

Там снова открываем выпадающий список "Active solution configuration " и находим завестную кнопочку "new". Создаем себе "WIN7 Debug"

Потом открываем свойства проекта - закладку Build и вводим в поле Conditional compilation symbols "WIN7"

Сохраняем конфигурацию. Прекрасно, теперь у нас есть 2 одинаковых конфигурации, которые различаются только наличием этих символов.

Переписываем ту строку кода в:

#if
WIN7 Browser.DialogWatcher.Add(new Windows7LogonDialogHandler(@"login", "password"));
#else
Browser.DialogWatcher.Add(new LogonDialogHandler(@"login", "password"));
#endif

Но и это еще не все! Создаем новый класс, пишем туда большой и страшный код, который я приведу внизу, а то не вижу тут тега спойлера. Класс называем "Windows7LogonDialogHandler .cs"
К нему то наша строчка и будет обращаться. Далее все просто. Если запускаем с ХР или Висты - конфигурацию выбираем "Debug", если с семерки - "WIN7 Debug". И все работает!

Ага. Щазззз. Проблемы могут и не кончиться)) Итак, мы прогуглили это дело, нашли тот скрипт для класса "Windows7LogonDialogHandler .cs" , который будет ниже, запустили (у программиста моего тоже семерка на рабочем компе) - работает! Ура?

Прихожу домой, запускаю - открывает окошко логина и... Ничего туда не вводит. Ну хоть не вбивает логин и пароль, как сумашедший, и то хлеб... Но блин, неудобно же! На компе фильмы смотреть, в игры играть и тд и тп, а как поработать надо - ноутбук включать. Одно дело, когда ты там тесты запустил и сидишь себе, играешься на основном компьютере. А когда ты туда лезешь их писать... Согласитесь, неприятно.

Прихожу на работу. Снова копируем мой проект (мои компы его через Dropbox получают, чтобы мне с флешкой каждый день не бегать), ставим у программиста на семерке - работает! Прихожу домой - не работает :(

Подключаю товарищей, дебажим код и что выясняется? У коллеги англ семерка, а у меня - русская. Поменяла в коде три названия, если у вас англ семерка, вам надо будет сменить "Другая учетная запись", "Пользователь" и "Пароль" на англ наименования... А я надеюсь, что мне не придется запускать свои тесты на английской семерке )))))

Хотя надо что-то придумать с этим. Нехорошо. Можно просто еще одну конфигурацию, в принципе, сделать... Надо ли? Ладно, посмотрим. Надеюсь, что когда вы начнете изучать автоматизацию, то проблему логина вы быстро обойдете, уже зная решение :))  А так в принципе это пока единственная проблема семерки, которую я знаю. Дальше все пойдет хорошо, главное, залогиниться!

Собственно, скрипт для русской версии:

using System.Linq;
using System.Windows.Automation;
using WatiN.Core.DialogHandlers;
using WatiN.Core.Native.Windows;


namespace UIModel
{

public class Windows7LogonDialogHandler : BaseDialogHandler{
{

private readonly string _username;
private readonly string _password;
AndCondition _conditions = new AndCondition(new PropertyCondition(AutomationElement.IsEnabledProperty, true),new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window));
readonly AndCondition _listCondition = new AndCondition(new PropertyCondition(AutomationElement.IsEnabledProperty, true),new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ListItem));
readonly AndCondition _editCondition = new AndCondition(new PropertyCondition(AutomationElement.IsEnabledProperty, true),new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit));
readonly AndCondition _buttonConditions = new AndCondition(new PropertyCondition(AutomationElement.IsEnabledProperty, true),new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button));

public Windows7LogonDialogHandler(string username, string password)
{
_username = username;
_password = password;
}
public override bool HandleDialog(Window window)
{
if (CanHandleDialog(window))
{
var win = AutomationElement.FromHandle(window.Hwnd);
var lists = win.FindAll(TreeScope.Children, _listCondition);
var buttons = win.FindAll(TreeScope.Children, _buttonConditions);
var another = (from AutomationElement list in lists
where list.Current.ClassName == "UserTile"
where list.Current.Name == "Другая учетная запись"
select list).First(); another.SetFocus();
foreach (var edit in from AutomationElement list in lists
where list.Current.ClassName == "UserTile"
select list.FindAll(TreeScope.Children, _editCondition)
into edits
from AutomationElement edit in edits
select edit)

{
if (edit.Current.Name.Contains("Пользователь"))
{
edit.SetFocus();

var usernamePattern = edit.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
if (usernamePattern != null) usernamePattern.SetValue(_username);
}


if (edit.Current.Name.Contains("Пароль"))
{
edit.SetFocus();
var passwordPattern = edit.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
if (passwordPattern != null) passwordPattern.SetValue(_password);
}
}

foreach (var submitPattern in from AutomationElement button in buttons
where button.Current.AutomationId == "SubmitButton"

select button.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern)
{
submitPattern.Invoke();
break;
}

return true;
}
return false;
}

public override bool CanHandleDialog(Window window)
{
return window.ClassName == "#32770";
}
}
}

7 комментариев:

  1. Мне кажется зря вы тест комплит так рано выбросили) просто немного другой подход нужен судя по тому что я понял из вашего описания о работе с ним.

    ОтветитьУдалить
  2. Да, но, согласитесь, 5к за лицензию и 0 - принципиально разные вещи)))

    + В тесткомплите я мало что понимала - он ищет все сам, делает какие то алиасы, а сама накодить я бы не смогла. И никто не поможет! Программистам он не знаком, так что малейшая помощь растягивается, пока он сам поймет, что тут да как.

    А так - в студии, на С#, любого спроси, поможет подскажет, на крайняк продебажим.

    Так что для меня тесткомплит вообще не вариант. По цене не вариант, и по поддержке тоже. Всегда лучше выбирать тот инструмент, по которому тебе смогут помочь.

    Но, надеюсь, кому-нибудь из будующих автоматизаторов этот текст поможет :)

    ОтветитьУдалить
  3. Я не совсем понял какой логин имеется в виду - логин в винду?
    Или логин/пароль, который просят когда открываешь в браузере "защищенный" ресурс?
    Можете пояснить, пожалуйста ?

    ОтветитьУдалить
  4. Логин/пароль в браузере, конечно, по кнопочке рекорд я не представляю, как можно записать логин в винду :)
    Ну и строчка кода начинается со слова browser...

    А зачем автоматизировать логин в винду? о_О

    ОтветитьУдалить
  5. IE 8, Win7 английская, данный код валится на:
    /////////
    var another = (from AutomationElement list in lists
    where list.Current.ClassName == "UserTile" where list.Current.Name == "Use another account"
    select list).First();
    /////////
    с ошибкой:

    "Exception was thrown while DialogWatcher called HandleDialog: System.InvalidOperationException: Sequence contains no elements
    at System.Linq.Enumerable.First[TSource](IEnumerable`1 source)"

    Если же закоментировать Where, то вводит логин, пароль, кликает сабмит, но окошко снова появляется - он снова вводит-кликает и так несколько раз, пока не сваливается. Короче логин не происходит.

    ОтветитьУдалить
  6. Сорри, это код работает (у меня был неправильный пароль), но я нашел более компактное решение, через AutoIt:

    AutoIt = new AutoItX3();
    AutoIt.WinWaitActive("Windows Security", "", 5));
    AutoIt.Send(user);
    AutoIt.Send("{TAB}");
    AutoIt.Send(password);
    AutoIt.Send("{TAB}{TAB}{ENTER}");

    Нужно только подключить длл-ку:
    using AutoItX3Lib;

    ОтветитьУдалить