Бот на C# Проблема с локатором, поиск элементов

Всем привет! Пишу бота на C# для сайта, столкнулся с такой проблемой: есть блок
<div class="b-games"> в этом блоке периодически появляются и исчезают другие блоки предстоящих событий (игр) <div class="b-game-block b-with-additional" data-id="16857">, собственно различаются они по по атрибутам data-id.
Проблема в том, как обращаться к элементам (кнопкам) самого нижнего блока?
Для этого постоянно придется переписывать атрибут data-id в CSS селекторе.

IWebElement element = Browser.FindElement(By.CssSelector("[data-id='16857'] .b-team1 button"));
element.Click();

Можно ли в CSS селектор добавить переменную через которую в программе будут подгружаться атрибуты(‘16857’, ‘16860’, …) или проблему нужно решать другим способом ?

Скриншот кода:
https://vk.com/deus_ex.human.revolution?z=photo314526898_456240065%2Falbum314526898_00%2Frev

String.Format("[data-id='{0}'] .b-team1 button", 16857)
или linq

FindElements(By.CssSelector("[data-id]")).First(o => Regex.IsMatch(o.GetAttribute("data-id", "16857").FindElement(By.CssSelector(".b-team1 button"))

Пробовал сделать так:

IWebElement element = Browser.FindElement(By.CssSelector("[id='bets-next'] .b-games [data-id='{0}'] .b-team1 button"));
 element.Click();

Поиск элемента не выполняется без цифрового атрибута в [data-id=’*****’] , это через linq можно сделать?
А как-то через переменную в программе можно передать этот атрибут типа:

int a = 16857;
.FindElement(By.CssSelector("[data-id='a'] ...

так не пробовали


int id = 16857;
IWebElement element = Browser.FindElement(
  By.CssSelector(
    String.Format("[id='bets-next'] .b-games [data-id='{0}'] .b-team1 button",id)
  )
);
element.Click();

Integer id = 16857;
IWebElement element2 = Browser.FindElements(
  By.CssSelector(
    "[id='bets-next'] .b-games [data-id]")).First(o => Regex.IsMatch(o.GetAttribute("data-id", id.ToString())).FindElement(
  By.CssSelector(".b-team1 button"));
element2.Click();
1 лайк

Если C# 6 (VS 2015+), то можно и так:

int gameId = 16857;
driver.FindElement(By.CssSelector($"[data-id='{gameId}']"));

Могу еще посоветовать посмотреть на Atata C#/.NET Framework. На нем кроме авто-тестов можно и боты писать. Ваш пример можно было бы описать в таком духе:

using Atata;
using _ = SampleApp.GamesPage;

namespace SampleApp
{
    public class GamesPage : Page<_>
    {
        [FindById("bets-next")]
        public ItemsControl<GameItem, _> NextGames { get; private set; }

        [ControlDefinition("div", ContainingClass = "b-game-block")]
        public class GameItem : Control<_>
        {
            public DataProvider<int, _> Id => Attributes.Get<int>("data-id");

            // Кнопка внутри '.b-game-block' элемента.
            public Button<_> SomeButton { get; private set; }
        }
    }
}

Ну и дальше работать со страницей:

int gameId = 16857;

Go.To<GamesPage>().
    NextGames.Items[x => x.Id == gameId].SomeButton.Click();
1 лайк

спасибо за syntax sugar.

а как в .net сделать эквивалент

elements.stream().collect(
				Collectors.toMap(o -> o.getAttribute("for"), . что-нибудь ));

?

Используя Linq так:

var elementsMap = elements.ToDictionary(x => x.GetAttribute("for"));
var elementsTextMap = elements.ToDictionary(x => x.GetAttribute("for"), x => x.Text);
1 лайк

спасибо!

Человек попросил что бы ему про :nth-child() и :last-child рассказали. А вы его лямбдами/фреймворками обфлудили.

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

по-моему это спорный вопрос как лучше длинный XPath или кусок Functional Programming посередине. пожалуйста обоснуйте

Первый вопрос топик-стартера, а все дальнейшие рассуждения - “как?”.[quote=“sergueik, post:10, topic:13757, full:true”]
по-моему это спорный вопрос как лучше длинный XPath или кусок Functional Programming посередине. пожалуйста обоснуйте
[/quote]

Это настолько простой вопрос, что хочу услышать ваши мысли в разрезе производительности, читабельности и количества строко-кода на единицу действия - “найти элемент”.

vmaxim XPath - даже не знаю как это правильно сказать по русски - does not belong to the program. поэтому он acceptable для тестов которые сами генерируются в массе автоматически или (augeas) в DSL “языках” типа
chef или puppet которые не являются языками программирования
или в IDE

а для framework в котором собственно строится фасад для таких массовых тестов XPath подход кажется хуже чем код. вопрос по моему - просто или же “намного” хуже.

Окей.
Варианты с [quote=“sergueik, post:10, topic:13757”]
Functional Programming посередине
[/quote]

,на примере страницы топик-стартера, будут примерно в 9 раз медленнее прямого поиска по локатору.
Вас не смущает порядок разницы и чем это может “аукнуться”, если подобные принципы заложены в ядро фреймворка?
Для примера, взятие атрибута на ios+simulator+appium будет стоить вам примерно одну секунду - итого 9 секунд “в никуда”.

Такое дело, не подскажете ли как можно записать аргументы `"data-id"` в массив для работы с ним в программе? 
<div id ="bet-next" class="time-manage">
	<div class="b-games">
		<div class="b-game-block" data-id="16990">...</div>
		<div class="b-game-block" data-id="17006">...</div>
		<div class="b-game-block" data-id="16987">...</div>
	</div>
</div>

по поводу быстроты спору нет . отладка коде помоему гораздо легче на явыке программирования на кот тест написан

Пример на Java но суть та же …
допустим решается quiz на http://suvian.in/selenium/3.7correspondingRadio.html и первое что приходит это

WebElement inputElement = driver
  .findElement(By.xpath(
      "//li[contains(text(), 'India')]//input[@type='radio'][@name='country']"))

а потом для закрепления отладки добавляется что-нибудь типа

WebElement inputElement = driver
    .findElements(By.xpath(
        "//li[contains(text(), 'India')]//input[@type='radio'][@name='country']"))
    .stream()
    .filter(o -> o.findElement(By.xpath("..")).getText()
        .contains((CharSequence) "India"))
    .findFirst().orElseThrow(RuntimeException::new);



естественно внутрии предиката можно смотреть

String elementHTML = element.getAttribute("outerHTML");
System.err.println("Element: " + (elementHTML.length() > 120
    ? elementHTML.substring(0, 120) + "..." : elementHTML));

ну и т.п.
или напр. assrts вообще без xpath - так они доьлше проживут


// Assert
Optional<WebElement> result = driver.findElements(By.tagName("input"))
    .stream()
    .filter(o -> o.getAttribute("type").equalsIgnoreCase("radio"))
    .filter(o -> o.findElement(By.xpath("..")).getText().contains((CharSequence) "India"))
    .filter(o -> o.getAttribute("checked") != null).findFirst();
WebElement checkedRadioButton = (result.isPresent()) ? result.get() : null;
assertThat(checkedRadioButton, notNullValue());

когда все заработало как надо FP убирается и никому от этого плохо не делается

1 лайк

Спасибо! Помогло от части, аргументы"data-id" можно ли как-то в массив записать?

конечно естественно (извините снач было неправильно)

using System.Linq;

IEnumerable<IWebElement>elements = driver.FindElements(By.CssSelector("[data-id]"));			
int[] results = elements
    .TakeWhile( e => Regex.IsMatch(e.GetAttribute("data-id") , "[0-9]+" ))
    .Select(x => Int32.Parse(x.GetAttribute("data-id")))
    .ToArray<int>();

как уже отвечал evgeniy_Shunevych

Извиняюсь за назойливость, еще буквально маленький вопрос!
Пытаюсь обратиться к последнему элементу в блоке, но дело в том, что атрибуты в массив записываются в таком виде (17056, 17057, 17058, 17059, …), а там у каждого элемента атрибуты “data-id” разные, так что нужно так (17056, 17060, 17048, 17039, …)

Ни кому нет дела, как вы отлаживаете код,

тем более, что вы отлаживаете не код,а локатор - и это проще делать в AUT (browser|inspector|etc) - а не в тестах.
И вы уверены,что из этой лапши сможете собрать корректный локатор?

Прошло четыре дня, а вы до сих пор задаете вопрос “как” - а основной вопрос “зачем” оставляете “за кадром”. Вряд ли в этой ситуации можно рассчитывать на ответ.