【问题标题】:How to programmatically configure Chrome extension through Selenium WebDriver如何通过 Selenium WebDriver 以编程方式配置 Chrome 扩展
【发布时间】:2016-05-03 19:31:53
【问题描述】:

我需要在 Chrome 中安装和配置一个扩展,以在 Selenium 测试执行期间修改所有请求标头。我已经能够从 Saucelabs 中的 support article 中遵循一个示例,展示了如何在本地为 Firefox 执行此操作,但不确定如何为 Chrome 执行此操作。

extensions 的 ChromeDriver 文档只涉及安装它们,而不是配置。

问题

  • 有人可以向我指出一些解释如何完成此操作的文档或在此处发布示例吗?
  • 如何更新设置?
  • 如何找出任何给定扩展程序可用的设置属性?
  • 本地和远程执行之间是否有任何区别,因为这是我在使用 Firefox method 时遇到的问题之一?

计划是针对 SauceLabs 运行此程序。会尝试使用ModHeader chrome 扩展来设置所需的标头值。

编辑 1

尝试安装 MODHeader 扩展的 Chrome 版本,但遇到类似问题。能够在本地安装扩展,但在远程执行中会出现错误。

private static IWebDriver GetRemoteDriver(string browser)
{

    ChromeOptions options = new ChromeOptions();
    options.AddExtensions("Tools/Chrome_ModHeader_2_0_6.crx");

    DesiredCapabilities capabilities = DesiredCapabilities.Chrome();
    capabilities.SetCapability(ChromeOptions.Capability, options);


    capabilities.SetCapability("name", buildContext);
    capabilities.SetCapability(CapabilityType.BrowserName, "Chrome");
    capabilities.SetCapability(CapabilityType.Version, "");
    capabilities.SetCapability(CapabilityType.Platform, "Windows 10");
    capabilities.SetCapability("screen-resolution", "1280x1024");
    capabilities.SetCapability("username", "SaucelabsUserName");
    capabilities.SetCapability("accessKey", "SaucelabsAccessKey");
    capabilities.SetCapability("build", "BuildNumber");
    capabilities.SetCapability("seleniumVersion", "2.50.1");


    return new RemoteWebDriver(new Uri("http://ondemand.saucelabs.com/wd/hub"), capabilities);
}

SauceLabs 日志中显示的错误是

[1.968][INFO]: RESPONSE InitSession unknown error: cannot parse capability: chromeOptions
from unknown error: unrecognized chrome option: Arguments

【问题讨论】:

  • 快速提问以确认我的理解:您是否必须使用扩展程序而不是 Browsermob 代理来执行此操作,通过它您可以管道所有 Selenium 流量并重写请求/响应的大部分方面?我会尽量避免创建任何特定于浏览器的内容。
  • 您也需要使用ModHeader吗? WebRequest API (developer.chrome.com/extensions/webRequest) 并不复杂,因此部署自定义的专用扩展(您可以向其发送自己的消息)可能比尝试控制现有扩展要容易得多。
  • 感谢 cmets @AndrewRegan。快速查看 Browsermob,文档没有提到在 C# 环境中使用它的任何内容,所以这对我来说是不行的,但对其他人来说可能是一个不错的选择。 WebRequest API 建议似乎您必须创建一个 chrome 扩展才能获得此功能,这比我想作为解决方案介绍的要复杂得多。我得到了上面链接的 Firefox 方法,这是最简单的方法。在 Chrome 中应该可以直接执行类似的操作,但我找不到有关如何执行此操作的文档或示例。
  • 我提到 Browsermob 是因为这是我用于 x-browser req/resp 重写的东西(例如绕过基本身份验证警报)。它有一个 REST API,所以 Java 方面应该无关紧要,但我敢打赌有基于 C# 的等价物。只需要确保可以从 Saucelab 服务器访问代理。对我来说,没有任何特定于浏览器的组件很重要,但我理解如果你想在 FF 工作时专注于 Chrome。
  • Apache 代理是否适合您的目的? stackoverflow.com/questions/154441/… 。类似于您对 BrowserMob 所做的事情,但可能更容易,具体取决于您的用例。

标签: google-chrome selenium selenium-webdriver google-chrome-extension saucelabs


【解决方案1】:

由于您提到问题主要出在远程,并且我注意到您正在使用 SauceLabs,您是否从他们那里查看过这篇文章?

https://support.saucelabs.com/customer/en/portal/articles/2200902-creating-custom-firefox-profiles-and-chrome-instances-for-your-automated-testing

Installing an Firefox Extension such as Modify Headers(You would need download the .xpi file on your machine first):

DesiredCapabilities caps = new DesiredCapabilities();
FirefoxProfile profile = new FirefoxProfile();
profile.addExtension(new File("path\of\Modify Headers xpi file"));
profile.setPreference("general.useragent.override", "UA-STRING");
profile.setPreference("extensions.modify_headers.currentVersion", "0.7.1.1-signed");
profile.setPreference("modifyheaders.headers.count", 1);
profile.setPreference("modifyheaders.headers.action0", "Add");
profile.setPreference("modifyheaders.headers.name0", "X-Forwarded-For");
profile.setPreference("modifyheaders.headers.value0", "161.76.79.1");
profile.setPreference("modifyheaders.headers.enabled0", true);
profile.setPreference("modifyheaders.config.active", true);
profile.setPreference("modifyheaders.config.alwaysOn", true);
profile.setPreference("modifyheaders.config.start", true);
caps.setCapability(FirefoxDriver.PROFILE, profile);

NOTE: If you trying to do the same using C#, you would need to use the ToBase64String() method.

【讨论】:

  • 我在我的问题中链接到那篇文章,并提到我能够让它在本地的 Firefox 中运行。在我的另一个链接中,我也设法让它在 Firefox 中远程工作。此问题涉及在 Chrome 中远程安装扩展程序。
【解决方案2】:
    public void AddHeaderChrome()
    {
    ChromeOptions  options = new ChromeOptions();
    options.addExtensions(new File("C:\\Downloads\\ModHeader_v2.0.9.crx"));
     DesiredCapabilities capabilities = DesiredCapabilities.internetExplorer();

    capabilities.setCapability(CapabilityType.options);
    // launch the browser
    WebDriver driver = new ChromeDriver(options);
    String HeadersName[]=new String[10];
    String HeadersValue[]=new String[10];;
    int length;
    if(ConfigDetails.HeadersName.contains(","))
    {
    HeadersName=ConfigDetails.HeadersName.split(",");
    HeadersValue=ConfigDetails.HeadersValue.split(",");
    length=HeadersName.length;
    }
    else
    {
       HeadersName[0]=ConfigDetails.HeadersName; 
       HeadersValue[0]=ConfigDetails.HeadersValue;
       length=1;
    }   
    int field_no=1;
    for(int i=0;i<length;i++)
    {
    driver.get("chrome-extension://idgpnmonknjnojddfkpgkljpfnnfcklj/popup.html");
    driver.findElement(By.xpath("//input[@id='fl-input-"+field_no+"']")).sendKeys(HeadersName[i]);
    driver.findElement(By.xpath("//input[@id='fl-input-"+(field_no+1)+"']")).sendKeys(HeadersValue[i]);
    field_no+=2
    }

【讨论】:

    【解决方案3】:

    Chrome 上的扩展程序具有恒定的唯一 ID。

    您可以使用 selenium 网络驱动程序导航到 chrome-extension://&lt;EXTENSION_UUIF&gt;/options.html,这里 options.html 是您定义的首选项页面。

    然后执行一个 sn-p 脚本来更改存储在chrome.storage.local 中的设置。

    【讨论】:

      【解决方案4】:

      我设法在 Saucelabs 的 Chrome 浏览器上安装了一个扩展程序,如下所示:

      ChromeOptions options = new ChromeOptions();
      options.addExtensions(new File("/path/to/myextrension.crx"));
      DesiredCapabilities capabilities = new DesiredCapabilities();
      capabilities.setCapability(ChromeOptions.CAPABILITY, options);
      capabilities.setBrowserName(DesiredCapabilities.chrome().getBrowserName());
      
      // Rest of capabilities config (version, platform, name, ...)
      
      WebDriver driver = new RemoteWebDriver(new URL("http://saucelabs-url/wd/hub"), capabilities);
      

      【讨论】:

        【解决方案5】:

        我找到了这个问题的解决方案。它适用于我的 Selenium GRID 和远程 Chrome 浏览器。 首先,我将解压后的 ModHeader(版本 1.2.4)扩展存储在我的项目资源中。看起来是这样的

        如果我需要在 Chrome 中修改标题,请执行以下步骤:

        1) 将带有扩展名的文件夹从资源解压到临时文件夹中

        2) 在 header.json 中设置标题键和值

        3) 使用 Java 将此扩展打包为 zip 文件

        4) 将 zip 文件添加到 ChromeOptions

        public static IDriver getDriverWithCustomHeader(List<HeaderElement> headerList) {
            Logger.info(StringUtils.buildString("Create new instance of Driver with header."));
            IDriver driver;
            DesiredCapabilities capabilities;
            switch (GlobalConfig.getInstance().getDriverType()) {
                case CHROME:
                    // define path to resources
                    String unpackedExtensionPath = FileUtils.getResourcePath("chrome_extension", true);
                    // setting  headers for extension in unpackaged kind
                    FileUtils.writeToJson(StringUtils.buildString(unpackedExtensionPath, File.separator, "header.json"), headerList);
                    // packing prepared extension to ZIP with crx extension
                    String crxExtensionPath = ZipUtils.packZipWithNameOfFolder(unpackedExtensionPath, "crx");
                    // creating capability based on packed extension
                    capabilities = CapabilityFactory.getChromeCapabilitiesWithExtension(crxExtensionPath);
                    driver = new AppiumDriver(GlobalConfig.getInstance().getHost(), GlobalConfig.getInstance().getPort(),
                            capabilities);
                    break;
                default:
                    throw new CommonTestRuntimeException("Unsupported Driver Type for changing head args.");
            }
        
            drivers.add(driver);
            if (defaultDriver != null) {
                closeDefaultDriver();
            }
        
            defaultDriver.set(driver);
            return driver;
        }
        

        文件工具

        public static String getResourcePath(String resourceName, boolean isDir) {
            String jarFileName = new File(FileUtils.class.getClassLoader().getResource(resourceName).getPath()).getAbsolutePath()
                    .replaceAll("(!|file:\\\\)", "");
            if (!(jarFileName.contains(".jar"))) {
                return getResourcePath(resourceName);
            }
            if (isDir) {
                return getDirPath(resourceName);
            }
            return getFilePath(resourceName);
        }
        
        private static String getResourcePath(String resourceName) {
            String resourcePath = FileUtils.class.getClassLoader().getResource(resourceName).getPath();
            if (platformIsWindows()) {
                resourcePath = resourcePath.substring(1);
            }
            return resourcePath;
        }
        
        private static boolean platformIsWindows() {
            boolean platformIsWindows = (File.separatorChar == '\\') ? true : false;
            return platformIsWindows;
        }
        
        private static String getDirPath(String dirName) {
            JarFile jarFile = null;
            //check created or no tmp directory
            //and if the directory created already we return "it + dirName"
            //else we create tmp directory and copy target resources
            if (directoryPath.get() == null) {
                //set directory path for each thread
                directoryPath.set(Files.createTempDir().getAbsolutePath());
            }
            //copying resources
            if (!new File(directoryPath.get() + File.separator + dirName.replaceAll("/", "")).exists()) {
                try {
                    List<JarEntry> dirEntries = new ArrayList<JarEntry>();
                    File directory = null;
                    String jarFileName = new File(FileUtils.class.getClassLoader().getResource(dirName).getPath()).getParent()
                            .replaceAll("(!|file:\\\\)", "").replaceAll("(!|file:)", "");
                    jarFile = new JarFile(URLDecoder.decode(jarFileName, "UTF-8"));
                    Enumeration<JarEntry> entries = jarFile.entries();
                    while (entries.hasMoreElements()) {
                        JarEntry jarEntry = entries.nextElement();
                        if (jarEntry.getName().startsWith(dirName)) {
                            if (jarEntry.getName().replaceAll("/", "").equals(dirName.replaceAll("/", ""))) {
                                directory = new File(directoryPath.get() + File.separator + dirName.replaceAll("/", ""));
                                directory.mkdirs();
                            } else
                                dirEntries.add(jarEntry);
                        }
                    }
                    if (directory == null) {
                        throw new CommonTestRuntimeException(StringUtils.buildString("There is no directory ", dirName,
                                "in the jar file"));
                    }
                    for (JarEntry dirEntry : dirEntries) {
                        if (!dirEntry.isDirectory()) {
                            File dirFile = new File(directory.getParent() + File.separator + dirEntry.getName());
                            dirFile.createNewFile();
                            convertStreamToFile(dirEntry.getName(), dirFile);
                        } else {
                            File dirFile = new File(directory.getParent() + File.separator + dirEntry.getName());
                            dirFile.mkdirs();
                        }
                    }
                    return directory.getAbsolutePath();
                } catch (IOException ex) {
                    ex.printStackTrace();
                } finally {
                    try {
                        jarFile.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                throw new CommonTestRuntimeException("There are problems in creation files in directory " + directoryPath);
            } else {
                return directoryPath.get() + File.separator + dirName.replaceAll("/", "");
            }
        }
        
        private static String getFilePath(String fileName) {
            try {
                String[] fileType = fileName.split("\\.");
                int typeIndex = fileType.length;
                File file = File.createTempFile(StringUtils.generateRandomString("temp"),
                        StringUtils.buildString(".", fileType[typeIndex - 1]));
                file.deleteOnExit();
                convertStreamToFile(fileName, file);
                return file.getAbsolutePath();
            } catch (IOException e) {
                e.printStackTrace();
            }
            throw new CommonTestRuntimeException("Impossible to get file path");
        }
        
        private static void convertStreamToFile(String resourceFileName, File file) throws IOException {
            try (InputStream in = FileUtils.class.getClassLoader().getResourceAsStream(resourceFileName);
                 BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF8"));
                 FileOutputStream fos = new FileOutputStream(file);
                 OutputStreamWriter fileOutputStreamWriter = new OutputStreamWriter(fos, "UTF8");
                 BufferedWriter fileWriter = new BufferedWriter(fileOutputStreamWriter);
            ) {
                String line = null;
                while ((line = reader.readLine()) != null) {
                    fileWriter.write(line + "\n");
                }
            }
        }
        
        public static void writeToJson(String jsonFilePath, Object object) {
            try {
                Gson gson = new Gson();
                FileWriter fileWriter = new FileWriter(jsonFilePath);
                fileWriter.write(gson.toJson(object));
                fileWriter.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        

        ZipUtils

        public static String packZipWithNameOfFolder(String folder, String extension) {
            String outZipPath = folder + "." + extension;
            try {
                try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(outZipPath))) {
                    File file = new File(folder);
                    doZip(file, zos);
                }
            } catch (IOException e) {
                throw new CommonTestRuntimeException("Fail of packaging of folder. ", e);
            }
            return outZipPath;
        }
        
        private static void doZip(File dir, ZipOutputStream out) throws IOException {
            for (File f: dir.listFiles()) {
                if (f.isDirectory()) {
                    doZip(f, out);
                } else {
                    out.putNextEntry(new ZipEntry(f.getName()));
                    try (FileInputStream in = new FileInputStream(f)) {
                        write(in, out);
                    }
                }
            }
        }
        
        private static void write (InputStream in, OutputStream out) throws IOException {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = in.read(buffer)) >= 0) {
                out.write(buffer, 0, len);
            }
        }
        

        CapabilityFactory.getChromeCapabilitiesWithExtension(...)

        public static DesiredCapabilities getChromeCapabilitiesWithExtension(String crxExtensionPath) {
            DesiredCapabilities chromeCapabilities = getChromeCapabilities();
            Logger.info("Extension path: " + crxExtensionPath);
            ChromeOptions options = new ChromeOptions();
            options.addExtensions(new File(crxExtensionPath));
            options.addArguments("--start-maximized");
            chromeCapabilities.setCapability(ChromeOptions.CAPABILITY, options);
            return chromeCapabilities;
        }
        

        【讨论】:

          【解决方案6】:
           public void AddHeaderFirefox(FirefoxProfile profile)
           {
           String directory = System.getProperty("user.dir");
           FirefoxProfile profile = new FirefoxProfile(); 
           try
           {
           profile.addExtension(new File(directory+"/modify-headers-0.7.1.1.xpi"));
           }
           catch(IOException e)
           {
            System.out.println(e);
           }
           String HeadersName[]=new String[10];
           String HeadersValue[]=new String[10];
          
           if(ConfigDetails.HeadersName.contains(",") && ConfigDetails.HeadersValue.contains(","))
           {
           HeadersName=ConfigDetails.HeadersName.split(",");
           HeadersValue=ConfigDetails.HeadersValue.split(",");
           length=HeadersName.length;
                         }
          
           You have to parametrise the header USING Split function of java to set 
           multiple headers.
          
           for(int i=0;i<length;i++)
           {
           profile.setPreference("modifyheaders.headers.count",i+1);
           profile.setPreference("modifyheaders.headers.action"+i, "Add");
           profile.setPreference("modifyheaders.headers.name"+i,HeadersName[i]);
           profile.setPreference("modifyheaders.headers.value"+i,HeadersValue[i]);
           profile.setPreference("modifyheaders.headers.enabled"+i, true);
           profile.setPreference("modifyheaders.config.active", true);
           profile.setPreference("modifyheaders.config.alwaysOn", true);
          

          }

          【讨论】:

            猜你喜欢
            • 2019-03-10
            • 2015-08-09
            • 2017-05-10
            • 2012-04-30
            • 1970-01-01
            • 1970-01-01
            • 2014-10-22
            • 2011-10-11
            相关资源
            最近更新 更多