如何使用 Selenium 处理 html5 约束验证弹出窗口?

How to handle html5 constraint validation pop-up using Selenium?

图片说明了一切。 单击 "LOG IN" 按钮后,会出现 "Please fill out this field." 弹出消息。它看起来像一些 javascript 东西。 我想使用 Selenium 获取此弹出消息的文本。 有可能吗?这是电子邮件输入字段的html:

<input autocorrect="none" autocapitalize="none" spellcheck="false" autofocus="autofocus" class="cell small-21 form-text required" data-drupal-selector="edit-name" aria-describedby="edit-name--description" type="text" id="edit-name" name="name" value="" size="60" maxlength="60" required="required" aria-required="true" placeholder="Email or Username">

P.S。当弹出消息出现时,浏览器开发工具中的 "event" 指示显示在电子邮件字段 html 附近。

没有看到html,我不能完全自信地告诉你。

不过,这应该是非常容易的(编辑:famous last words)。这是两件事之一:

1) 输入框的标题属性。将鼠标悬停在某物上,或者(如果旨在)将焦点放在具有 title 属性的元素上,会在这样的框中显示标题文本。虽然盒子肯定已经风格化了。

2) 这是一个自定义 div/other html,它连接到涉及该输入字段的 "focus" 事件的事件。

两者都需要不同的方式来获取文本。因为它们是不同的,所以我需要知道所涉及的html。请打开浏览器的开发者工具,并检查电子邮件字段。然后,复制此元素周围的 html,包括上面显示的工具提示文本。

一旦你粘贴 html,答案就会很简单。

编辑: 我完全被难住了。我查看了 html,搜索了事件列表和 javascript 文件。没有对该工具提示中显示的文本的引用。

此外,它呈现为 "title" attribute-based 工具提示。它不是 div。然而,标题并不仅仅存在于元素的 DOM 中。我不知道开发人员为这个页面做了什么魔术,让这一切发生了。我是一名网络开发人员,也是一名自动化工程师,这超出了我的范围。它可能只是我以前没有遇到过的东西,或者我可能只是错过了一些让我看到它时也会觉得自己很愚蠢的东西。

因此,我强烈建议您直接询问此页面的开发人员,他们做了什么使该标题显示为原样(不存在于 html 中,并且未在 [=52] 中引用=] 任何地方)。

编辑 #2: 这里是我提到的 last-resort 图像识别。它使用 AForge,它很容易作为 dll 引入。如果使用 Visual Studio,您可以将其作为 nuget 包获取。

using AForge.Imaging;
using System.Drawing;
using System.IO;
using OpenQA.Selenium;

public static class AssertImages
{

    //97.5% match expected. 100% is too strict, and practicly impossible. The lower the value you provide, the more possibly it is for a false match.
    //Never go below 75%. False matches above 75% are generally only possible if the baseline image is very generic, such as a basic shape or colored item.
    public const float BASE_EXPECTED_MATCH_PERCENT = 0.975f; 

    /// <summary>
    /// Determines if a provided baseline image exists within a screenshot of the current browser window .
    /// </summary>
    /// <param name="relativePathToBaseline"> Pass the relative url to the baseline image we are searching for.</param>
    /// <param name="matchExpected">Optional parameter that modifies the expected percent match. Use this when a baseline image may be stretched, different colored, or otherwise slightly modified from provided baseline.</param>
    /// <returns></returns>
    public static bool DoesScreenshotContain(string relativePathToBaseline, float matchExpected = BASE_EXPECTED_MATCH_PERCENT)
    {

        //Save screenhot as source image in jpeg format. Then, read jpeg as bitmap.
        Screenshot ss = ((ITakesScreenshot)BaseTest.Driver).GetScreenshot();
        string tempFile = Path.Combine(Path.GetTempPath(), "temp_screenshot.jpg");
        ss.SaveAsFile(tempFile, ScreenshotImageFormat.Jpeg);
        Bitmap sourceImage = new Bitmap(new MemoryStream(File.ReadAllBytes(tempFile)));
        Bitmap template = new Bitmap(Path.Combine(Reporting.BaseDirectory, "Suite", "ImageRecognition", "Baselines", relativePathToBaseline));
        //Find baseline in template image.
        ExhaustiveTemplateMatching tm = new ExhaustiveTemplateMatching(BASE_EXPECTED_MATCH_PERCENT);
        return tm.ProcessImage(sourceImage, template).Length > 0;

    }

}

然后,为工具提示中的文本拍照以用作基线。如下所述,我非常犹豫是否将其用作最终解决方案。更好的选择是找到一种方法来使用 Selenium 或直接 javascript,或者通过比我有更好答案的人,或者在编写此代码的开发人员的帮助下。话虽这么说,如果您需要立即可用的东西,在找到更好的解决方案之前,这应该会给您一些即时的满足感。

我以前也遇到过这种类型的页面,当时我和UI开发人员讨论过。 UI 上显示的错误是本机浏览器支持,如果该字段留空,每个浏览器都会默认显示此错误消息。

这是在 HTML5 中引入的,如果元素的 html 结构中有属性 required="required",则浏览器将默认显示您收到的错误消息.因此,如果您还检查您的元素,required="required" 也会出现在您元素的 html 中。

通常,网站不使用此功能,因为它们在页面上使用自定义的错误消息,但在您的情况下,它被使用了。因此,无法使用 selenium 检测此错误消息,因为错误消息不存在于 UI 或代码中的任何位置。因此,要么您要求您的开发人员更改实现,要么您需要从您的测试套件中跳过这些验证。

您所指的 popupConstraint API's element.setCustomValidity() 方法的结果。

Note: HTML5 Constraint validation doesn't remove the need for validation on the server side. Even though far fewer invalid form requests are to be expected, invalid ones can still be sent by non-compliant browsers (for instance, browsers without HTML5 and without JavaScript) or by bad guys trying to trick your web application. Therefore, like with HTML4, you need to also validate input constraints on the server side, in a way that is consistent with what is done on the client side.

解决方案

要检索从 element.setCustomValidity() 方法产生的文本,您可以使用以下任一方法 :

  • 使用 PythonMozillaCssSelector :

    • 代码块:

      from selenium import webdriver
      from selenium.webdriver.support.ui import WebDriverWait
      from selenium.webdriver.support import expected_conditions as EC
      from selenium.webdriver.common.by import By
      
      driver = webdriver.Firefox(executable_path=r'C:\Utility\BrowserDrivers\geckodriver.exe')
      driver.get("https://accounts.us1.advisor.ws/user/login")
      email_username = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "input.cell.small-21.form-text.required#edit-name[name='name']")))
      print(email_username.get_attribute("validationMessage"))
      
    • 控制台输出:

      Please fill out this field.
      
  • 使用JavaChromeXpath:

    • 代码块:

      import org.openqa.selenium.By;
      import org.openqa.selenium.WebDriver;
      import org.openqa.selenium.WebElement;
      import org.openqa.selenium.chrome.ChromeDriver;
      import org.openqa.selenium.chrome.ChromeOptions;
      import org.openqa.selenium.support.ui.ExpectedConditions;
      import org.openqa.selenium.support.ui.WebDriverWait;
      
      public class validationmessage {
      
          public static void main(String[] args) {
      
              System.setProperty("webdriver.chrome.driver", "C:\Utility\BrowserDrivers\chromedriver.exe");
              ChromeOptions options = new ChromeOptions();
              options.addArguments("start-maximized");
              options.addArguments("disable-infobars");
              options.addArguments("--disable-extensions"); 
              WebDriver driver =  new ChromeDriver(options);
              driver.get("https://accounts.us1.advisor.ws/user/login");
              WebElement email_username = new WebDriverWait(driver, 20).until(ExpectedConditions.elementToBeClickable(By.xpath("//input[@class='cell small-21 form-text required' and @id='edit-name'][@name='name']")));
              System.out.println(email_username.getAttribute("validationMessage"));
          }
      }
      
    • 控制台输出:

      Please fill out this field.