一尘不染

[C#] [Selenium]如何拖放元素

selenium

我针对其运行的网站位于内部服务器上,因此无法提供链接,但是我可以发布一些单击“显示元素”时显示的相关代码。

有五个与此相关的元素:

  • 组1
  • 学生
  • 移动1
  • 组2
  • 移动2

此页面显示学生组,并允许用户在组之间拖动学生。每个组都有一个移动元素。棘手的部分是,仅当用户将学生元素拖到该组上时,任何给定组的“移动”按钮才会显示,而该学生并非来自该组。

目的是将学生转移到新的小组中,然后再移回原来的小组。

注意:学生XPath会在更改组时发生更改,我无法确认,但我相信移动按钮的XPath在隐藏和可见之间是不同的

我当前的代码:

IWebDriver driver = (IWebDriver)FeatureContext.Current["Driver"];
        Actions builder = new Actions(driver);
        IWebElement originalstudent = driver.FindElement(By.XPath("//*[@id=\"AMTeacherApp\"]/div/div/div[1]/div/div[3]/div/div/div[2]/div[2]/div[2]/div/div[3]/a[1]/div[1]/div"));
        IWebElement originalClass = driver.FindElement(By.XPath("//*[@id=\"AMTeacherApp\"]/div/div/div[1]/div/div[3]/div/div/div[2]/div[2]/div[2]/div/div[1]"));
        IWebElement newClass = driver.FindElement(By.XPath("//*[@id=\"AMTeacherApp\"]/div/div/div[1]/div/div[3]/div/div/div[2]/div[2]/div[3]/div[1]/div[1]"));
        IWebElement originalMove = driver.FindElement(By.XPath("//*[@id=\"AMTeacherApp\"]/div/div/div[1]/div/div[3]/div/div/div[2]/div[2]/div[2]/div/div[2]/div[1]/div"));
        IWebElement newMove = driver.FindElement(By.XPath("//*[@id=\"AMTeacherApp\"]/div/div/div[1]/div/div[3]/div/div/div[2]/div[2]/div[3]/div[1]/div[2]/div[1]/div"));
        builder.ClickAndHold(originalstudent);
        builder.MoveToElement(newClass);
        builder.Release(newMove);
        builder.Build().Perform();
        IWebElement save = driver.FindElement(By.XPath("//*[@id=\"AMTeacherApp\"]/div/div/div[1]/div/div[3]/div/div/div[1]/div/div/button[2]"));
        builder.Click(save);
        builder.Build().Perform();
        //assert group2 has 1 student and group 1 has 3 students
        IWebElement newstudent = driver.FindElement(By.XPath("//*[@id=\"AMTeacherApp\"]/div/div/div[1]/div/div[3]/div/div/div[2]/div[2]/div[3]/div/div[3]/a/div[1]/div"));
        builder.ClickAndHold(newstudent);
        builder.MoveToElement(originalClass);
        builder.Release(originalMove);
        builder.Click(save);
        builder.Build().Perform();

此代码不会移动任何元素,并且在尝试查找保存时会失败,因为未进行任何更改(鉴于没有任何移动,这是预期的)。

页面中的相关代码:

  <div class="column  small-5 filter">
     <div class="row groups-header">
        <!-- ngIf: !model.configData.groupingMode.autoRegroup -->
        <div ng-if="!model.configData.groupingMode.autoRegroup" class="column small-8 ng-scope">
        </div>
        <!-- end ngIf: !model.configData.groupingMode.autoRegroup -->
        <!-- ngIf: model.configData.groupingMode.autoRegroup -->
        <div class="column small-4 left-delimiter no-regroup" ng-class="{'no-regroup' : !model.configData.groupingMode.autoRegroup}">
           <button type="button" class="light" ng-click="model.addGroup()" ng-disabled="!model.canAddGroup()">Add group
           </button>
        </div>
     </div>
     <!-- ngRepeat: group in model.groupingData -->
     <div class="group ng-scope" ng-repeat="group in model.groupingData">
******<div index="1" class="drop-outer" ui-on-drop="model.onDrop(group)" ui-drag-enter="model.dragOver(group)" ui-drag-leave="model.dragLeave(group)">
           <div class="button radius group-btn" ng-style="{'background-color': group.color}" ng-click="model.toggleState(group)" ui-on-drop="model.onMoveDrop(group)" 
           ui-drag-enter="moveActive = true;model.autoScroll(el);" ui-drag-leave="moveActive = false;" style="background-color: rgb(0, 155, 159);">
           <div class="left group-label ng-binding">Group 1 | 4  
              <span ng-show="group.students.length !== 1" class="">Students
              </span>
              <span ng-show="group.students.length === 1" class="ng-hide">Student
              </span>
           </div>
           <div class="right group-collapser" ng-hide="group.students.length == 0">
              <!-- ngIf: group.collapsed --> 
              <!-- ngIf: !group.collapsed -->
              <span class="glyph-chevron-collapsed ng-scope" ng-if="!group.collapsed">
              </span>
              <!-- end ngIf: !group.collapsed -->
           </div>
        </div>
        <div ng-show="group.showAddContainers" class="row drop-container ng-hide">
           <div ui-on-drop="model.onMoveDrop(group)" class="column" ng-class="{'active':moveActive}">
**********<div class="move">Move
              </div>
           </div>
           <div ui-on-drop="model.onCopyDrop(group)" class="column">
              <div class="copy">Copy
              </div>
           </div>
        </div>
        <!-- ngIf: !group.collapsed -->
******<div class="row students slide-animation drag-elements ng-scope" ng-if="!group.collapsed">
           <!-- ngRepeat: student in group.students -->
           <a no-chrome-href="" title="AM2Paper S" alt="AM2Paper S" class="student-btn-cont fade-animation ng-scope" ng-repeat="student in group.students" 
           ui-draggable="true" on-drag-begin="model.dragStart(student, group)" drag="student" drag-class="student-btn-cont student-btn-cont-dragged" 
           on-drop-success="model.dropSuccessHandler($index,group)" draggable="true" style="">
              <div ng-style="{'background-color': group.color}" class="radius column student-btn" style="background-color: rgb(0, 155, 159);">
************<div class="left student-label ng-isolate-scope" rl-display-name="" long-name="AM2Paper S" style="font-size: 9px;">AM2Paper S
                 </div>
              </div>
              <div class="dots dots-line">
              </div>
           </a>
           <!-- end ngRepeat: student in group.students -->
           <a no-chrome-href="" title="AMTest A" alt="AMTest A" class="student-btn-cont fade-animation ng-scope" ng-repeat="student in group.students" 
           ui-draggable="true" on-drag-begin="model.dragStart(student, group)" drag="student" drag-class="student-btn-cont student-btn-cont-dragged" 
           on-drop-success="model.dropSuccessHandler($index,group)" draggable="true" style="">
              <div ng-style="{'background-color': group.color}" class="radius column student-btn" style="background-color: rgb(0, 155, 159);">
                 <div class="left student-label ng-isolate-scope" rl-display-name="" long-name="AMTest A" style="font-size: 11px;">AMTest A
                 </div>
              </div>
              <div class="dots dots-line">
              </div>
           </a>
           <!-- end ngRepeat: student in group.students -->
           <a no-chrome-href="" title="AMTestPaper A" alt="AMTestPaper A" class="student-btn-cont fade-animation ng-scope" ng-repeat="student in group.students" 
           ui-draggable="true" on-drag-begin="model.dragStart(student, group)" drag="student" drag-class="student-btn-cont student-btn-cont-dragged" 
           on-drop-success="model.dropSuccessHandler($index,group)" draggable="true" style="">
              <div ng-style="{'background-color': group.color}" class="radius column student-btn" style="background-color: rgb(0, 155, 159);">
                 <div class="left student-label ng-isolate-scope" rl-display-name="" long-name="AMTestPaper A" style="font-size: 9px;">AMTestPaper...
                 </div>
              </div>
              <div class="dots dots-line">
              </div>
           </a>
           <!-- end ngRepeat: student in group.students -->
           <a no-chrome-href="" title="AM2Online S" alt="AM2Online S" class="student-btn-cont fade-animation ng-scope" ng-repeat="student in group.students" 
           ui-draggable="true" on-drag-begin="model.dragStart(student, group)" drag="student" drag-class="student-btn-cont student-btn-cont-dragged" 
           on-drop-success="model.dropSuccessHandler($index,group)" draggable="true" style="">
              <div ng-style="{'background-color': group.color}" class="radius column student-btn" style="background-color: rgb(0, 155, 159);">
                 <div class="left student-label ng-isolate-scope" rl-display-name="" long-name="AM2Online S" style="font-size: 9px;">AM2Online S
                 </div>
              </div>
              <div class="dots dots-line">
              </div>
           </a>
           <!-- end ngRepeat: student in group.students -->
        </div>
        <!-- end ngIf: !group.collapsed -->
        <div ng-show="group.showRemoveContainers" class="row drop-container ng-hide">
           <div class="column remove" ui-on-drop="model.onRemoveDrop(group)">
              <div class="remove ng-binding">Remove from Group 1
              </div>
           </div>
        </div>
     </div>
     <!-- ngIf: group.students.length == 0 -->
     </div>
     <!-- end ngRepeat: group in model.groupingData -->
     <div class="group ng-scope" ng-repeat="group in model.groupingData">
******<div index="2" class="drop-outer" ui-on-drop="model.onDrop(group)" ui-drag-enter="model.dragOver(group)" ui-drag-leave="model.dragLeave(group)">
           <div class="button radius group-btn" ng-style="{'background-color': group.color}" ng-click="model.toggleState(group)" ui-on-drop="model.onMoveDrop(group)" 
           ui-drag-enter="moveActive = true;model.autoScroll(el);" ui-drag-leave="moveActive = false;" style="background-color: rgb(109, 48, 146);">
              <div class="left group-label ng-binding">Group 2 | 0  
                 <span ng-show="group.students.length !== 1" class="">Students
                 </span>
                 <span ng-show="group.students.length === 1" class="ng-hide">Student
                 </span>
              </div>
              <div class="right group-collapser ng-hide" ng-hide="group.students.length == 0">
                 <!-- ngIf: group.collapsed --> 
                 <!-- ngIf: !group.collapsed -->
                 <span class="glyph-chevron-collapsed ng-scope" ng-if="!group.collapsed">
                 </span>
                 <!-- end ngIf: !group.collapsed -->
              </div>
           </div>
           <div ng-show="group.showAddContainers" class="row drop-container ng-hide">
              <div ui-on-drop="model.onMoveDrop(group)" class="column" ng-class="{'active':moveActive}">
************<div class="move">Move
                 </div>
              </div>
              <div ui-on-drop="model.onCopyDrop(group)" class="column">
                 <div class="copy">Copy
                 </div>
              </div>
           </div>
           <!-- ngIf: !group.collapsed -->
           <div class="row students slide-animation drag-elements ng-scope" ng-if="!group.collapsed" style="">
              <!-- ngRepeat: student in group.students -->
           </div>
           <!-- end ngIf: !group.collapsed -->
           <div ng-show="group.showRemoveContainers" class="row drop-container ng-hide">
              <div class="column remove" ui-on-drop="model.onRemoveDrop(group)">
                 <div class="remove ng-binding">Remove from Group 2
                 </div>
              </div>
           </div>
        </div>
        <!-- ngIf: group.students.length == 0 -->
        <div class="empty-group-label ng-scope" ng-if="group.students.length == 0">
           <span class="left">Drag student icons into this Group
           </span> 
           <a href="" class="right" ng-click="model.removeGroup($index)">Delete this group
           </a>
        </div>
        <!-- end ngIf: group.students.length == 0 -->
     </div>
     <!-- end ngRepeat: group in model.groupingData -->
  </div>

星号标记相关元素。让我知道您是否需要上下文相关的其他信息。


阅读 224

收藏
2020-06-26

共1个答案

一尘不染

Selenium的拖放操作在HTML5中效果不佳,因此我求助于使用jquery拖动元素。

我将这些用作参考:https :
//gist.github.com/rcorreia/2362544
http://elementalselenium.com/tips/39-drag-
and-drop

结果如下:

public void WhenAStudentIsMovedToANewGroup(string action, string student, string group)
    {
        WaitForAngular();
        IWebDriver driver = (IWebDriver)FeatureContext.Current["Driver"];

        /*
         * Load a version of jQuery that we can access
         */
        driver.Manage().Timeouts().SetScriptTimeout(TimeSpan.FromSeconds(10));
        IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
        js.ExecuteAsyncScript(loadJQuery, jQueryUrl);

        string dragEntity = string.Format("[title=\"{0}\"]", student);
        string target = string.Format("[ui-on-drop=\"model.onMoveDrop(group)\"]:contains({0})  ~ div > div .{1}", group, action);


        string javaScriptString = string.Format("{0}$('{1}').simulateDragDrop({{ dropTarget: '{2}'}});", dragAndDropHelper, dragEntity, target);

        //Execute the drag and drop against the HTML5
        js.ExecuteScript(javaScriptString);

    }

    const string dragAndDropHelper = @"(function( $ ) {
    $.fn.simulateDragDrop = function(options) {
            return this.each(function() {
                    new $.simulateDragDrop(this, options);
            });
    };
    $.simulateDragDrop = function(elem, options) {
            this.options = options;
            this.simulateEvent(elem, options);
    };
    $.extend($.simulateDragDrop.prototype, {
            simulateEvent: function(elem, options) {
                    /*Simulating drag start*/
                    var type = 'dragstart';
                    var event = this.createEvent(type);
                    this.dispatchEvent(elem, type, event);

                    /*Simulating drop*/
                    type = 'drop';
                    var dropEvent = this.createEvent(type, {});
                    dropEvent.dataTransfer = event.dataTransfer;
                    this.dispatchEvent($(options.dropTarget)[0], type, dropEvent);

                    /*Simulating drag end*/
                    type = 'dragend';
                    var dragEndEvent = this.createEvent(type, {});
                    dragEndEvent.dataTransfer = event.dataTransfer;
                    this.dispatchEvent(elem, type, dragEndEvent);
            },
            createEvent: function(type) {
                    var event = document.createEvent(""CustomEvent"");
                    event.initCustomEvent(type, true, true, null);
                    event.dataTransfer = {
                            data: {
                            },
                            setData: function(type, val){
                                    this.data[type] = val;
                            },
                            getData: function(type){
                                    return this.data[type];
                            }
                    };
                    return event;
            },
            dispatchEvent: function(elem, type, event) {
                    if(elem.dispatchEvent) {
                            elem.dispatchEvent(event);
                    }else if( elem.fireEvent ) {
                            elem.fireEvent(""on""+type, event);
                    }
            }
    });
})(jQuery);";



    const string loadJQuery = @"(function(jqueryUrl, callback) {
if (typeof jqueryUrl != 'string') {
    jqueryUrl = 'https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js';
}
if (typeof jQuery == 'undefined') {
    var script = document.createElement('script');
    var head = document.getElementsByTagName('head')[0];
    var done = false;
    script.onload = script.onreadystatechange = (function() {
        if (!done && (!this.readyState || this.readyState == 'loaded' 
                || this.readyState == 'complete')) {
            done = true;
            script.onload = script.onreadystatechange = null;
            head.removeChild(script);
            callback();
        }
    });
    script.src = jqueryUrl;
    head.appendChild(script);
}
else {
    callback();
}
})(arguments[0], arguments[arguments.length - 1]);";
2020-06-26