Looping and deleting folders in Kendo Treeview in automatic tests

12 posts, 0 answers
  1. Daniele Bruno
    Daniele Bruno avatar
    22 posts
    Member since:
    Apr 2009

    Posted 17 Dec 2015 Link to this post

    Hello there,

    I am working with Telerik Test Studio for my company, and the tool is proving to be very effective in managing our web applications testing needs.

    Anyway I am still learning and I've encountered some problems working with dynamically generated HTML tags and IDs.

    My webapp employs Kendo Treeview: what I need to do is end up with a runnable test that cycles through all the folders and subfolders of a Kendo Treeview and deletes them one after the other, unregarding the single UIIDs it finds (in a way that it could be ran again without modifying it).

    Up until now I've correctly managed to access a specific folder in the treeview (using the DOM features of the test recorder/runner) to delete it just once, but my problem relies in the dynamic naming of the single HTML elements which build up the treeview.

    If i record a test that deletes a specific folder, since it uses a delete link specific for one UIID, that test step from that moment on would be useless: I will not be able to run through it again to delete another folder in the same spot with the same name, because it would actually have different ID from the one with which I've recorded that step.

    I need to end up with a test that, every time I run it, doesn't crash at the step to delete a treeview folder because he's not locating one specific folder with a specific (expired) UIID, but that deletes them all in order, without caring about the dynamically labeled attributes of the scripted elements it deletes.

     

    I hope I wasn't too obscure and that I managed to explain my rather simple problem (which is nevertheless making me go nuts).

    Thank you to anyone who can help.

    bye

  2. Cody
    Admin
    Cody avatar
    3360 posts

    Posted 18 Dec 2015 Link to this post

    Hello Daniele,

    We can iterate through the entire Treeview using code like this:
    KendoTreeView treeView = Pages.something.something;
    foreach (KendoTreeNode treeNode in treeView.AllNodes)
    {
        // Do whatever action is necessary to delete this node
    }

    Problem here is that I don't know what it takes to folder or node. Every application is a custom application is going to be some custom element to locate and click on in order to delete it. I need to see a screenshot of your UI and the DOM in order to figure out how we can locate the correct element to delete current folder for now.

    Regards,
    Cody
    Telerik
     
    Quickly become an expert in Test Studio, check out our new training sessions!
    Test Studio Trainings
     
  3. Daniele Bruno
    Daniele Bruno avatar
    22 posts
    Member since:
    Apr 2009

    Posted 21 Dec 2015 in reply to Cody Link to this post

    First of all, many thanks to you Cody for having taken the time to see through my issue here.

    I have attached 2 edited screenshots:

    in the first one, you get to see how the treeview is shown to the user, here we have a simple 3-level tree, with a root, some children and the first of these with a child of its own ("a1" inside "a")

    next to a folder, there's a short icon-menu that appears when the user hovers on the element with the mouse, with 4 basic controls: delete, rename, refresh and permissions.

    as you asked, I've added a second screenshot showing the DOM: I have obtained the image using the "record test" function of Test Studio.

    then, on the left I've highlighted the single <span> element of Kendo Treeview which is located when you hover on a folder on the front-end and then use the "locate in DOM" option in the software; on the right side, I have shown which part is active with the same location method when you hover the "X" for the "delete" option of the appearing folder menu (the other options are clearly visible).

    as you can easily see, the IDs of folders and even deletion links are marked with dynamically generated UIIDs, which point to a specific element of the Kendo Treeview.

    They stay the same until the element gets removed.

    And this is exactly my problem: I need to put up a working test in Test Studio which can be run mutiple times, if I simply record an interaction with my application in which I delete the desired folders, as soon as I run it again the first interested step crashes, because the page cannot find the deletion link marked with a UIID which is no more in the DOM.

    What I need to do is to end up with a runnable test that deletes the folders, one at a time, from the innermost ones to the outermost, unregarding of their IDs, so that it could be executed more than one time. And, up until now, I didn't manage to make it.

    I hope my explaination is not too obscure and that you get what I mean.

    Thank you very much again for your help

    see you

  4. Daniele Bruno
    Daniele Bruno avatar
    22 posts
    Member since:
    Apr 2009

    Posted 21 Dec 2015 in reply to Daniele Bruno Link to this post

    actually, the screens are ordere alphabetically, so what I call in the post "first" screenshot is the second one "treeview_fe" and the other is "treeview_dom".
  5. Cody
    Admin
    Cody avatar
    3360 posts

    Posted 21 Dec 2015 Link to this post

    Hi Daniele,

    Thank you very much. That really helps. I suggest trying this code:
    KendoTreeView treeView = Pages.something.something;
    foreach (KendoTreeNode treeNode in treeView.AllNodes)
    {
        HtmlSpan deleteIcon = treeNode.Find.ByExpression<HtmlSpan>("id=^delete-folder");
        deleteIcon.Click();
    }

    What this code will do is it will start by locating the Treeview itself then iterate through every single root node finding and clicking on the delete icon. In your application if you delete a root node that has sub nodes will it delete the entire set? If not then we have to go through the more complicated approach of traversing down all of the folders and deleting the nodes then moving back up and deleting the folders. The approach in the code sample I just gave is the easiest approach assuming that sub notes will be deleted when you delete the parent folder.

    You will also need to figure out what to put in place of Pages.something.something. You will need to start by using add element to project. Once added then the first something will be the name of the page as shown in elements explorer and the 2nd something will be the name of the element as shown in elements explorer.

    Let me know if you need further assistance.

    Regards,
    Cody
    Telerik
     
    Quickly become an expert in Test Studio, check out our new training sessions!
    Test Studio Trainings
     
  6. Daniele Bruno
    Daniele Bruno avatar
    22 posts
    Member since:
    Apr 2009

    Posted 22 Dec 2015 in reply to Cody Link to this post

    hello again!

     

    we are getting there :)

    unfortunately, I find myself exactly in the situation you described: for security reasons, a parent folder cannot be deleted if it has files or children folders within itself, that is why I was saying I needed the test to delete one folder at a time from the innermost to the outermost.

    So, what would the approach be?

    again, thank you very much for your kind and swift response!

    see you

  7. Cody
    Admin
    Cody avatar
    3360 posts

    Posted 22 Dec 2015 Link to this post

    Hi,

    In that case we have to get 5 times more complicated. Here's the code:
    [CodedStep(@"New Coded Step")]
    public void Scratch_HTML_Test_CodedStep()
    {
        // A Kendo TreeView is an HTML ul element
        KendoTreeView treeView = Pages.something.something;
     
        // Taverse every item under the root node
        HtmlUnorderedList ul = treeView.BaseElement.As<HtmlUnorderedList>();
        foreach (HtmlListItem item in ul.Items)
        {
            TraverseTreeNode(item.BaseElement.As<KendoTreeNode>());
        }
    }
     
    /// <summary>
    /// Delete the passed in node after traversing all subnodes, deleting them along the way,
    /// contained under this node
    /// </summary>
    /// <param name="treeView">The node to be traversed then deleted</param>
    private void TraverseTreeNode(KendoTreeNode treeView)
    {
        // Define the find expression used to locate the name of a node
        HtmlFindExpression nodeNameFindExpression = new HtmlFindExpression("tagname=span", "class=^k-in");
        // Locate the name of this node
        HtmlSpan nameSpan = treeView.Find.ByExpression<HtmlSpan>(nodeNameFindExpression);
     
        Log.WriteLine("Starting TraverseTreeNode for node: " + nameSpan.InnerText);
        HtmlUnorderedList ul = treeView.Find.ByTagIndex<HtmlUnorderedList>("ul", 0);
        // traverse through all subnodes contained underneath this node
        foreach (HtmlListItem item in ul.Items)
        {
            // Locate the name of the subnode
            HtmlSpan innerNameSpan = item.Find.ByExpression<HtmlSpan>(nodeNameFindExpression);
     
            // If this node is expandable, then expand it
            KendoTreeNode node = item.BaseElement.As<KendoTreeNode>();
            if (item.BaseElement.ContainsAttribute("aria-expanded"))
            {
                if (!node.Expanded)
                {
                    node.Expand();
                }
                TraverseTreeNode(node);
            }
            else
            {
                // We found a leaf (non-expandable node). Delete it.
                DeleteNode(innerNameSpan, node, "leaf");
            }
        }
        // Finally delete the node we just finished traversing
        DeleteNode(nameSpan, treeView, "folder");
    }
     
    /// <summary>
    /// Common function to delete a node
    /// </summary>
    /// <param name="nameSpan"></param>
    /// <param name="node"></param>
    /// <param name="nodeType"></param>
    private void DeleteNode(HtmlSpan nameSpan, KendoTreeNode node, string nodeType)
    {
        Log.WriteLine("Deleting a " + nodeType + ": " + nameSpan.InnerText);
        HtmlSpan deleteIcon = node.Find.ByExpression<HtmlSpan>("id=^delete-folder");
        deleteIcon.Click();
    }


    Regards,
    Cody
    Telerik
     
    Quickly become an expert in Test Studio, check out our new training sessions!
    Test Studio Trainings
     
  8. Daniele Bruno
    Daniele Bruno avatar
    22 posts
    Member since:
    Apr 2009

    Posted 23 Dec 2015 in reply to Cody Link to this post

    thank you A TON!

     

    I will try and make this work then I will tell you if I made it, many thanks for all your support and merry Christmas :)

  9. Cody
    Admin
    Cody avatar
    3360 posts

    Posted 23 Dec 2015 Link to this post

    Hello Danielle,

    You're welcome! Let us know if you need any further assistance. Merry Christmas and Happy New Year to you as well!


    Regards,
    Cody
    Telerik
     
    Quickly become an expert in Test Studio, check out our new training sessions!
    Test Studio Trainings
     
  10. Daniele Bruno
    Daniele Bruno avatar
    22 posts
    Member since:
    Apr 2009

    Posted 15 Feb in reply to Cody Link to this post

    hello again Cody,

    sorry for bothering you again after all this time, up until now I still haven't managed to make this automation work, I am probably making some stupid mistake...

    here you will find the code I am using, it's basically your own code with just the inclusion of the right element from my DOM instead of Pages.something.something (explored through the Test Studio tool and inserted in my Element repository):

     

    #endregion
     
    // Add your test methods here...
     
    [CodedStep(@"definisce treeview e nodi")]
    public void _10_Cancellazione_ambiente_di_test_CodedStep()
    {
        // A Kendo TreeView is an HTML ul element
        KendoTreeView treeView = Pages.CBoxEsploraCloud.TreeviewUnorderedList;
          
        // Taverse every item under the root node
        HtmlUnorderedList ul = treeView.BaseElement.As<HtmlUnorderedList>();
        foreach (HtmlListItem item in ul.Items)
        {
            TraverseTreeNode(item.BaseElement.As<KendoTreeNode>());
        }
         
    }  
     
    /// <summary>
    /// Delete the passed in node after traversing all subnodes, deleting them along the way,
    /// contained under this node
    /// </summary>
    /// <param name="treeView">The node to be traversed then deleted</param>
    private void TraverseTreeNode(KendoTreeNode treeView)
    {
        // Define the find expression used to locate the name of a node
        HtmlFindExpression nodeNameFindExpression = new HtmlFindExpression("tagname=span", "class=^k-in");
        // Locate the name of this node
        HtmlSpan nameSpan = treeView.Find.ByExpression<HtmlSpan>(nodeNameFindExpression);
     
        Log.WriteLine("Starting TraverseTreeNode for node: " + nameSpan.InnerText);
        HtmlUnorderedList ul = treeView.Find.ByTagIndex<HtmlUnorderedList>("ul", 0);
        // traverse through all subnodes contained underneath this node
        foreach (HtmlListItem item in ul.Items)
        {
            // Locate the name of the subnode
            HtmlSpan innerNameSpan = item.Find.ByExpression<HtmlSpan>(nodeNameFindExpression);
     
            // If this node is expandable, then expand it
            KendoTreeNode node = item.BaseElement.As<KendoTreeNode>();
            if (item.BaseElement.ContainsAttribute("aria-expanded"))
            {
                if (!node.Expanded)
                {
                    node.Expand();
                }
            TraverseTreeNode(node);
            }
            else
            {
                //We found a leaf (non-expandable node). Delete it.
                DeleteNode(innerNameSpan, node, "leaf");
            }
        }
        // Finally delete the node we just finished traversing
        DeleteNode(nameSpan, treeView, "folder");
    }
     
    /// <summary>
    /// Common function to delete a node
    /// </summary>
    /// <param name="nameSpan"></param>
    /// <param name="node"></param>
    /// <param name="nodeType"></param>
    private void DeleteNode(HtmlSpan nameSpan, KendoTreeNode node, string nodeType)
    {
        Log.WriteLine("Deleting a " + nodeType + ": " + nameSpan.InnerText);
        HtmlSpan deleteIcon = node.Find.ByExpression<HtmlSpan>("id=^delete-folder");
        deleteIcon.Click();
    }

    as I try to save the test, no errors are shown, as soon as I try to run it, the log tells me the program failed to compile at the very start, when we define the KendoTreeView variable (I set the line to bold, up here). The error log says (translated from my italian interface):

    "[PATH_OF_MY_TEST_CASE/NAME_OF_TEST_CASE]: Line 53: (CS0266) Impossible to convert in an implicit way the type 'ArtOfTest.WebAii.Controls.HtmlControls.HtmlControl' in 'Telerik.TestingFramework.Controls.KendoUI.KendoTreeView'. It is present an explicit conversion. Probable missing cast."

    It's as if it said to me: "this element is the wrong one, this one is not a HTML unordered list and you are treating it as such without a proper cast", but the tools to explore the DOM made me save that tag.

    Any ideas?

    again, thank you very much for all the trouble and for your support.

  11. Daniele Bruno
    Daniele Bruno avatar
    22 posts
    Member since:
    Apr 2009

    Posted 15 Feb in reply to Daniele Bruno Link to this post

    wait, something has changed: I MANAGED TO LAUNCH THE TEST CASE!

    ...and this is a HUGE step forward.

    The compile error didn't show up anymore after I worked around the Elements Repository and loaded back the page and pointed again to the right tag in the DOM directly editing the element.

     

    now, for the execution: the test does the simple steps as normally designed (simple NOT coded steps for authentication/login into the application), as soon as it gets to the coded step I obtain just the try to delete one folder, and then that's it, goes straight to the others simples teps at the end to log out, then it stops, the automation ends and the test is passed.

    so something is not right in the coded step and afterwards, it seems to actually run the code after the coded step, because it calls (just once, and without confirmation, because we didn't do that part) the function to delete a folder. but even like this it's strange: it should FIRST delete all the files inside the innermost folder, then delete it, go up one level, cycle through the outermost fodlers, delete their files and then themselves. right now it just seems to ask if the user really wants to delete the first outermost folder (and even if we hit "yes" it will not, because the folder is not empty).

    we are close, I can see that, but we are still missing something.

  12. Vanya Pavlova
    Admin
    Vanya Pavlova avatar
    2019 posts

    Posted 18 Feb Link to this post

    Hello Daniele,


    In order to avoid any additional roundtrips I believe that the best option here is to open a new support thread.

    You may attach your application there and we will do our best to provide you with an appropriate solution. 


    Regards,
    Vanya Pavlova
    Telerik
     
    Quickly become an expert in Test Studio, check out our new training sessions!
    Test Studio Trainings
     
Back to Top