CSharp WebDriver Screenshots Extensions library

screenshot
csharp
webdriver
Теги: #<Tag:0x00007f7b62a39690> #<Tag:0x00007f7b62a39550> #<Tag:0x00007f7b62a39410>

(Alexandr D.) #1

Добрый день, коллеги.

Вдохновлённый когда-то ашотом от Яндекса я решил сделать нечто подобное для C#.
Для меня это жизненно необходимо - делать скриншот всей страницы, а не только видимой области.

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

Nuget пакет: https://www.nuget.org/packages/Noksa.WebDriver.ScreenshotsExtensions/
GitHub: https://github.com/Noksa/WebDriver.Screenshots.Extensions

Для использования достаточно вызвать новую перегрузку метода расширения:

driver.TakeScreenshot(IScreenshotStrategy strategy)

Данный метод возвращает byte[].

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

Ключевое здесь - последним должен всегда идти new ScreenshotMaker() - т.к. именно данный класс непосредственно создает скриншот.
Либо же вы всегда можете сделать свою реализацию, если есть необходимость.

Версия 0.0.2:

Сейчас доступны следующие декораторы:

  • new CutterDecorator(ISceenshotStrategy strategy)
    Данный декоратор имеет обязательный настроечный метод:
    SetCuttingStrategy(ICuttingStrategy strategy).
    Данным методом вы можете указать что и как следует вырезать на скриншоте.
    По умолчанию есть одна реализованная стратегия new CutElementHeightOnEntireWidthThenCombine(IWebElement element) - используется для удаления шапок/футеров, как статичных, так и тех, что не исчезают при скроллинге страницы.
    Данная стратегия находит элемент, вырезает его по высоте по всей ширине скриншота, только при условии, что он находится во viewport.
    Если этот элемент был не на самом верху или не в самом низу, то склеивает изображения, удаляя образовавшуюся пустоту.

  • new VerticalCombineDecorator(IScreenshotStrategy strategy)
    Данный декоратор будет создавать скриншот всей страницы, а не только видимой области.
    Имеет настроечный метод SetWaitAfterScrolling(TimeSpan timeSpan), который отвечает за ожидание после скроллинга страницы перед снятием скриншота. По умолчанию 50 мс.

  • new OnlyElementDecorator(IScreenshotStrategy strategy)
    Делает скриншот только указанного элемента.
    Элемент нужно указать путём вызова метода SetElement(IWebElement element).

Вы можете комбинировать их так, как вам требуется. Т.е. например делать скриншот всей страницы, срезая футер или шапку, или и футер и шапку.
Но не стоит комбинировать вертикальное объединение и скриншот элемента, из этого не получится ничего хорошего :wink:

:warning:При использовании VerticalCombineDecorator он должен стоять первым

Примеры:

var ele = _driver.FindElement(By.XPath("//*[@id=\"hrId\""));
var arr = _driver.TakeScreenshot(new CutterDecorator(new ScreenshotMaker()).SetCuttingStrategy(new CutElementHeightOnEntireWidthThenCombine(ele)));
var vcs = new VerticalCombineDecorator(new ScreenshotMaker());
var screen = _driver.TakeScreenshot(vcs);

Скриншот конкретного элемента:

_driver.Navigate().GoToUrl("http://yandex.ru");
var ele = _driver.FindElement(By.Id("text"));
var vcs = new OnlyElementDecorator(new ScreenshotMaker()).SetElement(ele);
var screen = _driver.TakeScreenshot(vcs);

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

var diffImage = WdseImageComparer.CompareAndGetImage("C:\\diff1.png", "C:\\diff1.png");

Либо булево

var boolRes = WdseImageComparer.Compare("C:\\diff1.png", "C:\\diff1.png");
Первое изображение

image

Второе изображение

image

Изображение с отличием первого от второго

image

Соответственно если отличий не будет, то красного не будет :slight_smile:

Небольшой пример. Скриншот всей страницы:

3 mb file

Надеюсь кому-то это окажется полезным. :slight_smile:

Более подробная информация https://github.com/Noksa/WebDriver.Screenshots.Extensions/wiki


(Alexandr D.) #2

0.0.3 версия готова.

Ключевое изменение - теперь в аргументах вместо IWebElement надо передавать By.
Это ломающее изменение, к сожалению.
Придётся изменить аргументы, кто уже начал пользоваться.

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

Добавлено: https://github.com/Noksa/WebDriver.Screenshots.Extensions/issues/4
Инфо по использованию: https://github.com/Noksa/WebDriver.Screenshots.Extensions/wiki/ScreenshotMaker


(Alexandr D.) #3

0.0.3.2:
Добавлена возможность скрывать скроллбары во время снятия скриншота.
После того, как скриншот будет готов, скроллбары снова станут видимыми, по аналогии с сокрытием элементов.
Настройка доступна в классе ScreenshotMaker.

var scmkr = new ScreenshotMaker();
scmkr.RemoveScrollBarsWhileShooting();

(Nick) #4

Ещё длиннее не получилось сделать? :wink:

В шарпе есть extension методы, которые можно прикрутить к чему угодно. В смысле вместо этой простыни можно сделать IWebElement.TakeScreenShot(). То же самое касается экстеншена над IWebDriver, с тонной сигнатур, если надо удалять скроллбары, скринить не всё и т.д.


(Alexandr D.) #5

Напишите не в одну строчку, будет покороче. Ну или посмотрите что такое декораторы и как они работают, чтобы не удивляться длине строчки…

Да ладно? :slight_smile:

Можно, только тогда придётся аргументом передавать сам драйвер. И это выглядит как-то не очень.
Либо хранить ссылку на него в каком-то вспомогательном классе, что подразумевает под собой дополнительную обязательную строку кода для пользователя, как минимум.
Ну и это будет дополнительная точка входа, со всеми возможными последствиями.

Пример - если использовать метод расширения для IWebElement для скрытия элемента во время снятия скрина, тебе надо сначала найти этот элемент и только тогда вызывать метод на нём. Лишняя работа.

С подходом “делать метод с кучей сигнатур” - с таким подходом будет много методов/перегрузок, да ещё и у разных объектов (драйвер/элемент) - всё это надо как-то запоминать/знать или постоянно читать мануал.
Ну или будет один - с огромной кучей опциональных параметров… Это тоже самое что конструктор с 20 параметрами :slight_smile:

А так у тебя есть один объект и ты просто его оборачиваешь в нужное тебе поведение - наглядно, просто и понятно.


(Nick) #6

Ну если вы предлагаете мне почитать про декораторы, предложу в ответ прочитать как работает селениум :sunglasses:

Не очень это не знать, что драйвер можно получить из элемента :wink:

То есть 100500 врапперов (new CutterDecorator(new ScreenshotMaker()).SetCuttingStrategy(new CutElementHeightOnEntireWidthThenCombine) интуитивно понятны и делаются без мануала?

Я не лазил в код и могу конечно ошибаться, но что-то мне подсказывает, что все поведения можно обернуть в enum.

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

  1. всем пофиг :slight_smile:
  2. шарповых автоматизаторов очень мало, скиллов тоже (привет java сообществу)

Потому я и подгораю, когда вижу, что вы даете ошибочные советы (пруфов не будет) или выдаете подобные решения как супер удобные, ведя тему в 100 сообщений, объясняя как этим пользоваться.


(Alexandr D.) #7

Если вы дадите любой инструмент новичку, он без мануала всё равно его не осилит. Даже если там будет всего 5 методов.

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

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

Обернуть в enum, конечно же, можно. Так же как и сделать много разных методов или же один с опциональными параметрами.

Но это непродуктивный подход с точки зрения трудозатрат и поддержки.
Делать 100500 енумов под разные комбинации поведений? Че-то не очень.
А когда что-то новое появится, надо расширять енум, потом добавлять в свитчи… итд и тп
Это так же неудобно, как писать 100500 ифов при опциональных параметрах.

Это всё ваши домыслы.
Я нигде здесь не писал, что это очень удобно и вообще вне конкуренции.
Я всего лишь поделился с сообществом инструментом, который предоставляет доп. функционал для снятия скриншотов.

Если он вам не нравится - не пользуйтесь, я же не заставляю вас :slight_smile:

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

Для этого и пишутся мануалы, в общем-то.
Раз о мануалах зашла речь - я как раз на гитхабе проекта написал вики более подробно с примерами.
Либо никто не пользуется, либо вопросов нет, ибо никто пока не писал :slight_smile:

Ок :cowboy_hat_face:


(Nick) #8

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