Firebugの“ Copy XPath”と同等Internet Explorerで?
-
03-07-2019 - |
質問
Internet Explorer専用のWebアプリケーションがあります。
テストを自動化するためにできることを探っています。
Seleniumは優れたツールのように見えますが、リンクなどをアクティブにできるようにするには、リンクの場所を指定する必要があります。アプリケーションは、この種のテストを念頭に置いて作成されたものではないため、通常、キー要素に id
属性はありません。
問題ありません。XPath式を使用できます。しかし、ボタンなどの正しいXPathを見つけることは、ページのソースを調べることで行う場合、非常に苦痛です。
Firefox / Firebugでは、要素を選択し、「Copy XPath」を使用できます。式を取得します。
IE Developer Toolbarを持っていますが、イライラするほど近いです。目的の要素をクリックして選択し、それに関するあらゆる種類の情報を表示できます。しかし、XPathを決定する便利な方法がわかりません。
では、IEでこれを行う方法はありますか?
解決
ブックマークレットを使用します。関連するXPathが1つありますが、IEで機能するかどうかはわかりません。私は行かなければならないが、それをテストし、IEで動作する場合はそれを提供する。
ブックマークからのWeb開発者向けの2つのブックマークレットサイト: SubsimpleのブックマークレットおよびSquarefreeのブックマークレット。便利なものがたくさんあります...
[編集] OK、帰ってきました。私が持っていたブックマークレットは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を取得するには、要素を選択してブックマークレットをアクティブにする必要があります。
現在、ブックマークレットのバージョン(ブックマークレットビルダーのおかげで):
IE
(IEは非常に長いブックマークレットが好きではないので、2つの部分に分けなければなりませんでした(最大サイズはIEのバージョンによって異なります!)。 )
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();
他のヒント
ブックマークレットは困惑したPaulを使用しているので、私はそれらの使用法について少し説明を追加する必要があります。個別のメッセージでそれを行うことで、物が混ざらないようにします。
ブックマークレット(ファブレットとも呼ばれる)は、ブラウザーのアドレスバーに貼り付ける(他のURLと同様)ために現在のページで実行するように設計された小さなJavaScriptスクリプト(原文)です。
実行後(貼り付け、Enterキーを押す)、再利用するためにブックマークすることができます(IEでお気に入りに追加)。ブラウザが代わりに元のURLをブックマークする可能性があることに注意してください。ブックマークを編集し、URLをスクリプトに置き換える必要があります。
もちろん、URLバーに追加してすばやくアクセスすることもできます。
これらのスクリプトは、現在のページの一部であるように動作し、グローバルJS変数および関数、Domオブジェクトなどにアクセスします。
これらは、独創的な javascript:alert(" Hello world!");
のように非常に単純なものでも、上記のような非常に複雑なものでもかまいません。値を返す場合(または最後の式に値がある場合)、その値は現在のページを置き換えます。これを回避するには、 alert
(結果を表示する)でスクリプトを終了するか、スクリプトを関数定義でラップして、上記のようにこの関数を呼び出します。 (最後にvoid(0)を置くものもありますが、悪い習慣と見なされているのを見ました。)
関数ソリューションには、スクリプトのすべての変数をアプレットに対してローカルにするという利点があります(もちろん var
で宣言した場合)、ローカルページ上のスクリプトとの干渉/副作用を回避できます。これが、ラッピング関数がローカルスクリプトと衝突する可能性が低い名前を持つ必要がある理由でもあります。
一部のブラウザ(「IE」を参照)では、ファブレットのサイズを制限できます。バージョンによって長さが異なります(減少する傾向があります)。無駄な空白がすべて削除されるのはこのためです(上記のブックマーレットビルダーはこれに適しています)。通常行うnullとundefinedの明示的な比較を削除しました。また、ファブレットを2つに分割する必要がありました。最初の部分は関数を定義し(ページが変更/更新されない限り)、2番目の部分はそれを使用します。
特にユーザースクリプトを許可しないブラウザ(à la 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;
}
}
}