【问题标题】:Equivalent of Firebug's "Copy XPath" in Internet Explorer?相当于 Internet Explorer 中 Firebug 的“复制 XPath”?
【发布时间】:2010-09-17 21:50:04
【问题描述】:

我有一个仅限 Internet Explorer 的 Web 应用程序。

我正在探索我们可以做些什么来自动化测试。

Selenium 看起来是个不错的工具,但要能够激活链接等。我需要告诉它它们在哪里。该应用程序在构建时并未考虑到此类测试,因此关键元素上通常没有id 属性。

没问题,我想,我可以使用 XPath 表达式。但是,如果通过检查页面的源代码来为按钮找到正确的 XPath,那就太痛苦了。

使用 Firefox / Firebug,我可以选择元素,然后使用“复制 XPath”来获取表达式。

我有 IE 开发人员工具栏,但它关闭得令人沮丧。我可以单击以选择感兴趣的元素并显示有关它的各种信息。但我看不到任何方便的方法来确定它的 XPath。

那么有没有办法用 IE 做到这一点?

【问题讨论】:

  • 你说“仅限互联网浏览器”。这是否意味着您甚至无法在 Firefox 中打开应用程序?或者只是它需要在 IE 中完美运行,而不管它是否在其他浏览器中运行?
  • 更新了我的答案以提供代码。请注意,您必须选择一个元素来获取它的 XPath,这对于某些活动组件来说可能很烦人,但我认为它无论如何都是一个很好的起点。
  • 是的,它确实只有 IE。它甚至不会在 Firefox 中加载(该应用会显式检查浏览器类型)

标签: internet-explorer selenium firebug automated-tests


【解决方案1】:

我会使用书签。我有一个 XPath 相关的,但我不知道它是否适用于 IE。我得走了,但如果它在 IE 上运行,我会测试它并提供它。

我的书签中有两个供 Web 开发人员使用的书签网站:Subsimple's bookmarkletsSquarefree's Bookmarklets。那里有很多有用的东西...

[编辑] 好的,我回来了。我拥有的书签仅适用于 FF,并不是最佳的。我终于重写了它,尽管使用了原来的想法。找不到我找到它的地方了。

扩展的 JS:

function getNode(node)
{
  var nodeExpr = node.tagName;
  if (nodeExpr == null)  // Eg. node = #text
    return null;
  if (node.id != '')
  {
    nodeExpr += "[@id='" + node.id + "']";
    // We don't really need to go back up to //HTML, since IDs are supposed
    // to be unique, so they are a good starting point.
    return "/" + nodeExpr;
  }
// We don't really need this
//~   if (node.className != '')
//~   {
//~     nodeExpr += "[@class='" + node.className + "']";
//~   }
  // Find rank of node among its type in the parent
  var rank = 1;
  var ps = node.previousSibling;
  while (ps != null)
  {
    if (ps.tagName == node.tagName)
    {
      rank++;
    }
    ps = ps.previousSibling;
  }
  if (rank > 1)
  {
    nodeExpr += '[' + rank + ']';
  }
  else
  {
    // First node of its kind at this level. Are there any others?
    var ns = node.nextSibling;
    while (ns != null)
    {
      if (ns.tagName == node.tagName)
      {
        // Yes, mark it as being the first one
        nodeExpr += '[1]';
        break;
      }
      ns = ns.nextSibling;
    }
  }
  return nodeExpr;
}

var currentNode;
// Standard (?)
if (window.getSelection != undefined) 
  currentNode = window.getSelection().anchorNode;
// IE (if no selection, that's BODY)
else 
  currentNode = document.selection.createRange().parentElement();
if (currentNode == null)
{
  alert("No selection");
  return;
}
var path = [];
// Walk up the Dom
while (currentNode != undefined)
{
  var pe = getNode(currentNode);
  if (pe != null)
  {
    path.push(pe);
    if (pe.indexOf('@id') != -1)
      break;  // Found an ID, no need to go upper, absolute path is OK
  }
  currentNode = currentNode.parentNode;
}
var xpath = "/" + path.reverse().join('/');
alert(xpath);
// Copy to clipboard
// IE
if (window.clipboardData) clipboardData.setData("Text", xpath);
// FF's code to handle clipboard is much more complex 
// and might need to change prefs to allow changing the clipboard content.
// I omit it here as it isn't part of the original request.

您必须选择元素并激活小书签才能获取其 XPath。

现在,书签版本(感谢Bookmarklet Builder):

IE
(我不得不把它分成两部分,因为 IE 不喜欢很长的书签(最大大小取决于 IE 版本!)。你必须激活第一个(函数 def)然后是第二个。用 IE6 测试。 )

javascript:function getNode(node){var nodeExpr=node.tagName;if(!nodeExpr)return null;if(node.id!=''){nodeExpr+="[@id='"+node.id+"']";return "/"+nodeExpr;}var rank=1;var ps=node.previousSibling;while(ps){if(ps.tagName==node.tagName){rank++;}ps=ps.previousSibling;}if(rank>1){nodeExpr+='['+rank+']';}else{var ns=node.nextSibling;while(ns){if(ns.tagName==node.tagName){nodeExpr+='[1]';break;}ns=ns.nextSibling;}}return nodeExpr;}
javascript:function o__o(){var currentNode=document.selection.createRange().parentElement();var path=[];while(currentNode){var pe=getNode(currentNode);if(pe){path.push(pe);if(pe.indexOf('@id')!=-1)break;}currentNode=currentNode.parentNode;}var xpath="/"+path.reverse().join('/');clipboardData.setData("Text", xpath);}o__o();

FF

javascript:function o__o(){function getNode(node){var nodeExpr=node.tagName;if(nodeExpr==null)return null;if(node.id!=''){nodeExpr+="[@id='"+node.id+"']";return "/"+nodeExpr;}var rank=1;var ps=node.previousSibling;while(ps!=null){if(ps.tagName==node.tagName){rank++;}ps=ps.previousSibling;}if(rank>1){nodeExpr+='['+rank+']';}else{var ns=node.nextSibling;while(ns!=null){if(ns.tagName==node.tagName){nodeExpr+='[1]';break;}ns=ns.nextSibling;}}return nodeExpr;}var currentNode=window.getSelection().anchorNode;if(currentNode==null){alert("No selection");return;}var path=[];while(currentNode!=undefined){var pe=getNode(currentNode);if(pe!=null){path.push(pe);if(pe.indexOf('@id')!=-1)break;}currentNode=currentNode.parentNode;}var xpath="/"+path.reverse().join('/');alert(xpath);}o__o();

【讨论】:

  • 看起来很有趣,但我不知道如何添加书签 :( 你能指出一些可以解释这一点的东西吗?
  • 我希望你能得到这样的答案...添加书签非常简单:复制 javascript: 行(在我的 IE 标记的情况下一个接一个)并将其粘贴到地址栏中浏览器(并按回车键)。然后,您可以将其添加到收藏夹(它们也称为 favlet)以回调它们。然后运行 ​​JS。
  • 我得到“错误:ps 为空。源文件:javascript:function%...”现在这段代码在 FF 3.5.5 中失效了吗?
  • @Photodeus:哎呀!显然你是第一个测试 FF 版本的人......我有一个错字,一个 ps 而不是 ns(在 ns=ps.nextSibling 中)。固定在答案中。感谢您的报告。
  • 有趣的代码。我想知道这段代码是否可以用于测试 DOM 中元素的给定 XPath。例如。传入 XPath,如果找到则返回对 DOM 元素的引用,否则返回 null/undefined。像 document.getElementByXPath()、document.getElementsByXPath() 这样的东西。其他浏览器有技术可以做到这一点,但似乎相同的技术代码在 IE 上不起作用。 autumnator.wordpress.com/2013/05/02/…
【解决方案2】:

既然小书签的使用让 Paul 很困惑,我想我应该对它们的使用做一点介绍。我在单独的消息中这样做以避免混淆。

Bookmarklet(也称为 favlet)是一种小型 JavaScript 脚本(原文如此),旨在粘贴在浏览器的地址栏中(与任何其他 URL 一样),因此可以在当前页面上运行。
运行后(粘贴,按 Enter),您可以将其添加为书签以供重复使用(将其添加到 IE 中的收藏夹)。请注意,浏览器可能会将原始 URL 加入书签,然后您必须编辑书签并将 URL 替换为您的脚本。
当然,您也可以将其添加到 URL 栏中以便快速访问。

这些脚本就像是当前页面的一部分,访问全局 JS 变量和函数、Dom 对象等。
它们可以超级简单,比如开创性的javascript: alert("Hello world!");,也可以非常复杂,比如上面的那个。如果它返回一个值(或者如果最后一个表达式有一个值),则该值将替换当前页面。为避免这种情况,您可以使用alert 完成脚本(以显示结果)或将脚本包装在函数定义中并调用此函数,就像我在上面所做的那样。 (有些人还把 void(0); 放在最后,但我认为这被视为不好的做法。)

函数解决方案的优点是使脚本的所有变量都对小程序本地化(当然,如果使用var 声明),避免了对本地页面上的脚本的干扰/副作用。这也是为什么包装函数应该有一个不太可能与本地脚本冲突的名称。

请注意,某些浏览器(阅读:“IE”)可以限制 favlet 的大小,最大值。长度随版本变化(趋于减少)。这就是为什么所有无用的空格都被删除的原因(上面链接的书签生成器对此很有用),并且我删除了我通常使用的 null 和 undefined 的显式比较。我还必须将 favlet 分成两部分,第一部分定义一个函数(只要页面没有更改/刷新就一直存在),第二部分使用它。

有用的工具,特别是在不允许用户脚本(à la Greasemonkey)或没有此扩展程序的浏览器上。

【讨论】:

    【解决方案3】:

    我已将小书签代码重写为 C#,所以如果您觉得它有用,请使用它;-)

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Text;
    
    namespace Anotation.Toolbar
    {
        class XPath
        {
            public static string getXPath(mshtml.IHTMLElement element)
            {
                if (element == null)
                    return "";
                mshtml.IHTMLElement currentNode = element;
                ArrayList path = new ArrayList();
    
                while (currentNode != null)
                {
                    string pe = getNode(currentNode);
                    if (pe != null)
                    {
                        path.Add(pe);
                        if (pe.IndexOf("@id") != -1)
                            break;  // Found an ID, no need to go upper, absolute path is OK
                    }
                    currentNode = currentNode.parentElement;
                }
                path.Reverse();
                return join(path, "/");
            }
    
            private static string join(ArrayList items, string delimiter)
            {
              StringBuilder sb = new StringBuilder();
              foreach (object item in items)
              {
                if (item == null)
                    continue;
    
                sb.Append(delimiter);
                sb.Append(item);
              }
              return sb.ToString();
            }
    
            private static string getNode(mshtml.IHTMLElement node)
            {
                string nodeExpr = node.tagName;
                if (nodeExpr == null)  // Eg. node = #text
                    return null;
                if (node.id != "" && node.id != null)
                {
                    nodeExpr += "[@id='" + node.id + "']";
                    // We don't really need to go back up to //HTML, since IDs are supposed
                    // to be unique, so they are a good starting point.
                    return "/" + nodeExpr;
                }
    
                // Find rank of node among its type in the parent
                int rank = 1;
                mshtml.IHTMLDOMNode nodeDom = node as mshtml.IHTMLDOMNode;
                mshtml.IHTMLDOMNode psDom = nodeDom.previousSibling;
                mshtml.IHTMLElement ps = psDom as mshtml.IHTMLElement;
                while (ps != null)
                {
                    if (ps.tagName == node.tagName)
                    {
                        rank++;
                    }
                    psDom = psDom.previousSibling;
                    ps = psDom as mshtml.IHTMLElement;
                }
                if (rank > 1)
                {
                    nodeExpr += "[" + rank + "]";
                }
                else
                { // First node of its kind at this level. Are there any others?
                    mshtml.IHTMLDOMNode nsDom = nodeDom.nextSibling;
                    mshtml.IHTMLElement ns = nsDom as mshtml.IHTMLElement;
                    while (ns != null)
                    {
                        if (ns.tagName == node.tagName)
                        { // Yes, mark it as being the first one
                            nodeExpr += "[1]";
                            break;
                        }
                        nsDom = nsDom.nextSibling;
                        ns = nsDom as mshtml.IHTMLElement;
                    }
                }
                return nodeExpr;
            }
        }
    }
    

    【讨论】:

    • 那你会如何使用这个 C# 版本呢?
    • 我试过这段代码,它工作正常,只是当 HTML 元素为 INPUT mshtml.IHTMLElement ps = psDom as mshtml.IHTMLElement; 时它无法将 COM 对象转换为 IHTMLElement; 谁能指出原因?
    猜你喜欢
    • 2011-03-08
    • 2016-04-08
    • 1970-01-01
    • 2011-05-18
    • 1970-01-01
    • 2016-06-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多