В данной статье я расскажу, как отлавливать и закрывать любые окна в любой момент времени. При этом мы конечно же должны знать, что есть некое окно, которое может всплывать на сайте, это необходимо, чтобы в коде его указать. Сейчас речь идёт не о всплывающих сообщениях 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;