2008-10-18 9 views
11

J'ai une application Web Internet Explorer uniquement. J'explore ce que nous pouvons faire pour automatiser les tests. Le sélénium ressemble à un bon outil, mais pour pouvoir activer les liens, je dois dire où ils sont. L'application n'a pas été construite avec ce genre de test en tête, donc il n'y a généralement pas d'attributs id sur les éléments clés.Equivalent de "Copy XPath" de Firebug dans Internet Explorer?

Pas de problème, je pense, je peux utiliser des expressions XPath. Mais trouver le XPath correct pour, disons, un bouton, est une douleur royale si elle est faite en inspectant la source de la page. Avec Firefox/Firebug, je peux sélectionner l'élément puis utiliser "Copier XPath" pour obtenir l'expression. J'ai la barre d'outils de développement IE et il est frustrant de fermer. Je peux cliquer pour sélectionner l'élément d'intérêt et afficher toutes sortes d'informations à ce sujet. mais je ne vois pas de moyen pratique de déterminer le XPath pour cela.

Y a-t-il un moyen de le faire avec IE?

+0

Vous dites "Internet Explorer uniquement". Est-ce que cela signifie que vous ne pouvez même pas ouvrir l'application dans Firefox? Ou juste que cela doit fonctionner parfaitement dans IE, indépendamment du fait que cela fonctionne dans d'autres navigateurs? – pkaeding

+0

Mise à jour ma réponse pour donner le code. Notez que vous devez sélectionner un élément pour obtenir son XPath, cela peut être gênant pour certains composants actifs, mais je pense que ça peut être un bon point de départ quand même. – PhiLho

+0

Oui, c'est vraiment IE seulement. Il ne chargera même pas dans Firefox (l'application vérifie le type de navigateur explicitement) –

Répondre

7

J'utiliserais des bookmarklets. J'ai un XPath lié, mais je ne sais pas si cela fonctionne dans IE. Je dois y aller mais je vais le tester et le donner si ça marche sur IE.

Deux sites bookmarklet pour les développeurs Web à partir de mes signets: Subsimple's bookmarklets et Squarefree's Bookmarklets. Beaucoup de choses utiles là-bas ...

[EDIT] OK, je suis de retour. Le bookmarklet que j'avais était pour FF seulement, et n'était pas optimal. J'ai finalement réécrit, en utilisant des idées de l'original. Je ne peux pas trouver où je l'ai trouvé.

JS: Expanded

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. 

Vous devez sélectionner l'élément et activer le bookmarklet pour obtenir sa XPath.

maintenant, les versions bookmarklet (grâce à Bookmarklet Builder):

IE
(je devais casser en deux parties, parce que IE n'aime pas très longue Bookmarklets (taille maximale varie selon les versions IE!). Il faut activer le premier (def fonction), puis le second. Testé avec 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(); 
+0

Cela semble intéressant, mais je n'ai aucune idée de comment ajouter un bookmarklet :(Pouvez-vous me montrer à quelque chose qui explique cela? –

+0

J'espère que vous obtenez une telle réponse ... Pour ajouter un bookmarklet, c'est très simple: copiez la ligne javascript: (un par un dans le cas de mes markets IE) et collez-la dans la barre d'adresse du navigateur (et appuyez sur Entrée). les ajouter aux favoris (on les appelle aussi les faveurs) pour les rappeler, JS est alors exécuté – PhiLho

+0

J'obtiens "Erreur: ps est nul Fichier source: javascript: function% ..." Est-ce que ce code est maintenant défunt dans FF? 3.5.5? – Photodeus

3

Depuis l'utilisation de bookmarklet intrigué Paul, je pense que je devrais ajouter une petite introduction à leur utilisation. Je le fais dans un message séparé pour éviter de mélanger des choses. Les signets (également appelés favlets) sont de petits scripts JavaScript (sic) conçus pour être collés dans la barre d'adresse du navigateur (comme n'importe quelle autre URL) et ainsi s'exécuter sur la page en cours.
Après l'avoir exécuté (coller, appuyez sur Entrée), vous pouvez le mettre en signet pour le réutiliser (ajoutez-le aux favoris dans IE). Notez que le navigateur peut mettre en signet l'URL d'origine à la place, vous devez ensuite modifier le signet et remplacer l'URL par votre script.
Bien sûr, vous pouvez également l'ajouter à la barre d'URL pour un accès rapide.

Ces scripts agissent comme faisant partie de la page en cours, l'accès à des variables globales de JS et fonctions, objets Dom, etc.
Ils peuvent être super simple, comme le javascript: alert("Hello world!"); séminal ou assez complexe comme celle-ci. Si elle renvoie une valeur (ou si la dernière expression a une valeur), la valeur remplace la page en cours. Pour éviter cela, vous pouvez terminer le script avec alert (pour afficher un résultat) ou envelopper le script dans une définition de fonction et appeler cette fonction, comme je l'ai fait ci-dessus. (Certains vide également mis (0), à la fin, mais je l'ai vu est considéré comme une mauvaise pratique.)

La solution de fonction a l'avantage de rendre à l'applet locales toutes les variables du script (si elle est déclarée avec var , bien sûr), en évitant les interférences/effets secondaires avec des scripts sur la page locale. C'est aussi pourquoi la fonction d'emballage devrait avoir un nom peu susceptible d'entrer en conflit avec un script local.

Notez que certains navigateurs (lire: "IE") peuvent limiter la taille des favlets, le max. longueur variant avec la version (tendant à diminuer). C'est pourquoi tous les blancs inutiles sont supprimés (le créateur de bookmarlet lié ci-dessus est bon pour cela) et j'ai supprimé les comparaisons explicites avec null et indéfini que je fais habituellement. J'ai aussi dû diviser le favlet en deux, la première partie définissant une fonction (vivant tant que la page n'est pas changée/rafraîchie), la deuxième partie l'utilisant.

Outil utile, en particulier sur les navigateurs ne permettant pas les scripts utilisateur (à la Greasemonkey) ou sans cette extension.

2

J'ai réécrit le code bookmarklet en C#, donc si vous trouverez utile, utiliser ;-)

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; 
     } 
    } 
} 
+0

Comment utiliseriez-vous cette version C# alors? – David

+0

J'ai essayé ce code et il fonctionne bien, sauf qu'il ne parvient pas à convertir l'objet COM en IHTMLElement lorsque l'élément HTML est INPUT mshtml.IHTMLElement ps = psDom as mshtml.IHTMLElement; quelqu'un peut-il indiquer la raison? – Tejas

Questions connexes