Эквивалент Firebug "Копировать XPath" в Internet Explorer?
У меня есть только веб-приложение Internet Explorer.
Я изучаю, что мы можем сделать, чтобы автоматизировать тестирование.
Selenium выглядит хорошим инструментом, но чтобы иметь возможность активировать ссылки и т. Д., Я должен сказать, где они находятся. Приложение не было создано с учетом этого вида тестирования, поэтому, как правило, нет id
атрибуты на ключевых элементах.
Нет проблем, я думаю, я могу использовать выражения XPath. Но найти правильный XPath для, скажем, кнопки - это королевская боль, если сделать это путем проверки источника страницы.
С Firefox / Firebug я могу выбрать элемент, а затем использовать "Копировать XPath", чтобы получить выражение.
У меня есть панель инструментов разработчика IE, и она разочаровывает. Я могу нажать, чтобы выбрать интересующий элемент и отобразить всевозможную информацию о нем. но я не вижу никакого удобного способа определения XPath для него.
Так есть ли способ сделать это с IE?
3 ответа
Я бы использовал букмарклет. У меня есть один связанный XPath, но я не знаю, работает ли он в IE. Я должен идти, но я проверю это и дам это, если это работает на IE.
Два сайта закладок для веб-разработчиков из моих закладок: Закладки Subsimple и Закладки Squarefree. Там много полезного...
[РЕДАКТИРОВАТЬ] ОК, я вернулся. Букмарклет, который у меня был, был только для 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();
Поскольку использование букмарклета озадачило Пола, я должен добавить небольшое введение в их использование. Я делаю это в отдельном сообщении, чтобы избежать смешивания вещей.
Букмарклеты (также называемые favlets) - это небольшие JavaScript-скрипты (sic), предназначенные для вставки в адресную строку браузера (как и любой другой URL-адрес) и, следовательно, для запуска на текущей странице.
После запуска (вставьте, нажмите Enter), вы можете добавить его в закладки для повторного использования (добавить в избранное в IE). Обратите внимание, что браузер может добавить в закладки исходный URL-адрес, затем необходимо отредактировать закладку и заменить URL-адрес сценарием.
Конечно, вы также можете добавить его в строку URL для быстрого доступа.
Эти сценарии действуют как часть текущей страницы, обращаясь к глобальным переменным и функциям JS, объектам Dom и т. Д.
Они могут быть очень простыми, как семенной javascript: alert("Hello world!");
или довольно сложный, как тот, что выше. Если он возвращает значение (или если последнее выражение имеет значение), значение заменяет текущую страницу. Чтобы избежать этого, вы можете закончить скрипт alert
(для отображения результата) или оберните сценарий в определение функции и вызовите эту функцию, как я делал выше. (Некоторые также помещают void(0); в конце, но я видел, что это считается плохой практикой.)
Преимущество функционального решения заключается в том, что все переменные скрипта должны быть локальными для апплета (если объявлены с var
, конечно), избегая вмешательства / побочных эффектов со скриптами на локальной странице. Вот почему у функции обертки должно быть имя, которое вряд ли будет конфликтовать с локальным скриптом.
Обратите внимание, что некоторые браузеры (читай: "IE") могут ограничивать размер фавлетов, макс. длина зависит от версии (имеет тенденцию к уменьшению). Вот почему удаляются все бесполезные пробелы (для этого хорошо подходит создатель букмаркета, указанный выше), и я удалил явные сравнения со значениями null и undefined, которые я обычно делаю. Мне также пришлось разделить фавлет на две части: первая часть, определяющая функцию (действующая до тех пор, пока страница не была изменена / обновлена), вторая часть, использующая ее.
Полезный инструмент, особенно в браузерах, не допускающих пользовательских скриптов (как Greasemonkey) или без этого расширения.
Я переписал код букмарклета в 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;
}
}
}