В данной статье я расскажу, как отлавливать и закрывать любые окна в любой момент времени. При этом мы конечно же должны знать, что есть некое окно, которое может всплывать на сайте, это необходимо, чтобы в коде его указать. Сейчас речь идёт не о всплывающих сообщениях JavaScript, хотя думаю можно приспособить написанное ниже и под них.
Используемый мной стек технологий при написании автоматических тестов: C#, NUnit, Selenium WebDriver.
Всё началось с того, что у нас в компании используются регрессионные автоматические тесты наших рабочих сайтов. Тесты периодически в определённый момент начинают «падать». Так как у нас тесты делают скриншот страницы в момент падения теста, то я обнаружил, что постоянно попадаются новые не ведомые нам всплывающие рекламные баннеры, которые всплывают в неподходящие моменты, многократно и рандомно. Баннеры, которыми можно управлять с помощью cookie мы отключаем в самом начале запуска тестов создавая cookie, в которых указано, что баннер закрыт и его уже не надо открывать, но есть баннеры, которыми невозможно управлять cookie и они как проказа вечно всплывают, закрывая элементы сайтов, которые используют тесты. Код баннеров размещают на сайте наши маркетологи и разработчики с тестировщиками об этом узнают только при падении тестов (так повелось). За это я недолюбливаю наших маркетологов.
Знать где и когда появится баннер, о котором мы уже знаем невозможно. Они выпрыгивают как чёртики из табакерки. Если их закроешь на одной странице, то через определённое время он выпрыгнет на другой. Логики в их появлении также нет (пытался определить). В итоге возникла необходимость «уничтожать» эти баннеры. Но легко сказать да не легко сделать.
Возникающие проблемы:
— баннеры появляются на разных страницах и не в одно и то же время.
Мы не можем точно сказать, когда и где баннер появится, поэтому мы не можем в коде автоматических тестов чётко указать «открыть страницу оформления заказа» — «дождаться появления баннера» — «закрыть баннер». Он просто может не появиться, так как он появлялся на другой странице и теперь выскочит ещё где-то, но не там, где его пытаешься дождаться, а если не дождёмся, то тест упадёт. Эта ситуация заставила искать нестандартное решение. И тут в голову пришла мысль о многопоточности. Суть проста: в одном потоке выполняются тесты, в другом потоке мы отслеживаем появление баннеров и удаляем их. Сказано – сделано.
Тут вспоминаем многопоточность в C# и приступаем к реализации. Вспомнив многопоточность, мы понимаем, что нам в отдельном потоке необходимо запускать метод, в коде которого будет выполняться проверка на наличие баннера и его удаление. Проверка на наличие/видимость баннера — это ещё один метод… Итого нам нужно:
— написать методы проверки элемента на видимость или наличие;
— написать метод удаления всплывающих окон/баннеров;
— написать код вызова отдельного потока (см. ссылку выше).
У нас есть метод проверки наличия элемента «IsElementExists» и метод проверки видимости элемента «IsElementVisible», для наглядности мы их оба будем использовать (см.ссылку выше).
Далее создаём метод удаления всплывающих окон:
// Метод отлавливает указанные баннеры и закрывает/удаляет их. Используется в отдельном потоке. public void DelAllPopup() { IJavaScriptExecutor js = driver as IJavaScriptExecutor; for (; ;) // цикл без параметров - бесконечный { try { if (IsElementVisible(By.CssSelector("div.zloy-banner-1")) == true) // если баннер 1 видим, то... { js.ExecuteScript("$('.zloy-banner-1').remove();"); // удалить из HTML } if (IsElementExists(By.CssSelector("div.zloy-banner-2")) == true) // если код баннера 2 существует в HTML, то... { js.ExecuteScript("$('.zloy-banner-2').remove();"); // удалить из HTML } } catch (Exception) { /* ничего не делаем если ошибка в процессе выполнения */ } Thread.Sleep(10); // если не указать, то получать будем ошибку при выполнении тестов: System.Net.Sockets.SocketException : Обычно разрешается только одно использование адреса сокета... } }
Здесь мы:
— проверяем видим ли «баннер 1», если он видим удаляем его из кода HTML с помощью JavaScript (и он исчезает);
— проверяем наличие кода «баннера 2» в структуре HTML и если он есть, то удаляем его из кода HTML с помощью JavaScript.
Также обязателен в цикле «try-catch». О причине подробнее рассказываю в статье Почему NUnit Agent завершает работу с ошибкой.
Теперь нам необходимо перед запуском теста запускать отдельный поток, в котором отслеживаются и удаляются всплывающие окна/баннеры, о которых мы знаем.
public static Thread potok1; [OneTimeSetUp] // вызывается перед началом запуска всех тестов public void TestFixtureSetUp() { } [OneTimeTearDown] //вызывается после завершения всех тестов public void TestFixtureTearDown() { } [SetUp] // вызывается перед каждым тестом public void SetUp() { potok1 = new Thread(DelAllPopup); potok1.Start(); } [TearDown] // вызывается после каждого теста public void TearDown() { potok1.Abort(); // остановка потока }
Вот и всё. Теперь перед запуском каждого теста будет запускаться в отдельном потоке метод удаления ненужных элементов, а по завершению теста этот поток будет завершаться.
Подключения в моём проекте:
using System; using System.IO; using System.Threading; using System.Diagnostics; using NUnit.Framework; using Selenium; using OpenQA.Selenium; using OpenQA.Selenium.Support.UI; using OpenQA.Selenium.Chrome;