Click all anchor tags on page with given class, but cancel prior to navigation

纵饮孤独 提交于 2019-12-09 23:14:57

问题


Trying to automate some testing for some analytics tracking code, and I'm running into issues when I try passing links into the each() method.

I copied a lot of this from stackoverflow - how to follow all links in casperjs, but I don't need return the href of the link; I need to return the link itself (so I can click it). I keep getting this error: each() only works with arrays. Am I not returning an array?

UPDATE:

For each anchor tag that has .myClass, click it, then return requested parameters from casper.options.onResourceReceived e.g. event category, event action, etc. I may or may not have to cancel the navigation the happens after the click; I simply only need to review the request, and do not need the follow page to load.

Testing steps:

  1. click link that has .myClass
  2. look at request parameters
  3. cancel the click to prevent it from going to the next page.

I'm new to javascript and casper.js, so I apologize if I'm misinterpreting.

ANOTHER UPDATE: I've updated the code to instead return an array of classes. There are a few sketchy bits of code in this though (see comments inline).

However, I'm now having issues canceling the navigation after the click. .Clear() canceled all js. Anyway to prevent default action happening after click? Like e.preventDefault();?

var casper = require('casper').create({
    verbose: true,
    logLevel: 'debug'
});

casper.options.onResourceReceived = function(arg1, response) {

    if (response.url.indexOf('t=event') > -1) {
        var query = decodeURI(response.url);
        var data = query.split('&');
        var result = {};
        for (var i = 0; i < data.length; i++) {
            var item = data[i].split('=');
            result[item[0]] = item[1];
        }
        console.log('EVENT CATEGORY = ' + result.ec + '\n' +
            'EVENT ACTION = ' + result.ea + '\n' +
            'EVENT LABEL = ' + decodeURIComponent(result.el) + '\n' +
            'REQUEST STATUS = ' + response.status
        );

    }
};

var links;
//var myClass = '.myClass';

casper.start('http://www.leupold.com', function getLinks() {
    links = this.evaluate(function() {

        var links = document.querySelectorAll('.myClass');
        // having issues when I attempted to pass in myClass var.

        links = Array.prototype.map.call(links, function(link) {

            // seems like a sketchy way to get a class. what happens if there are multiple classes?
            return link.getAttribute('class');
        });

        return links;
    });
});

casper.waitForSelector('.myClass', function() {

    this.echo('selector is here');
    //this.echo(this.getCurrentUrl());
    //this.echo(JSON.stringify(links));

    this.each(links, function(self, link) {
        self.echo('this is a class : ' + link);
        // again this is horrible
        self.click('.' + link);
    });
});



casper.run(function() {
    this.exit();
});

回答1:


There are two problems that you're dealing with.

1. Select elements based on class

Usually a class is used multiple times. So when you first select elements based on this class, you will get elements that have that class, but it is not guaranteed that this will be unique. See for example this selection of element that you may select by .myClass:

  1. myClass
  2. myClass myClass2
  3. myClass myClass3
  4. myClass
  5. myClass myClass3

When you later iterate over those class names, you've got a problem, because 4 and 5 can never be clicked using casper.click("." + links[i].replace(" ", ".")) (you need to additionally replace spaces with dots). casper.click only clicks the first occurrence of the specific selector. That is why I used createXPathFromElement taken from stijn de ryck to find the unique XPath expression for every element inside the page context.

You can then click the correct element via the unique XPath like this

casper.click(x(xpathFromPageContext[i]));

2. Cancelling navigation

This may depend on what your page actually is.

Note: I use the casper.test property which is the Tester module. You get access to it by invoking casper like this: casperjs test script.js.

Note: There is also the casper.waitForResource function. Have a look at it.

2.1 Web 1.0

When a click means a new page will be loaded, you may add an event handler to the page.resource.requested event. You can then abort() the request without resetting the page back to the startURL.

var resourceAborted = false;
casper.on('page.resource.requested', function(requestData, request){
    if (requestData.url.match(/someURLMatching/)) {
        // you can also check requestData.headers which is an array of objects:
        // [{name: "header name", value: "some value"}]
        casper.test.pass("resource passed");
    } else {
        casper.test.fail("resource failed");
    }
    if (requestData.url != startURL) {
        request.abort();
    }
    resourceAborted = true;
});

and in the test flow:

casper.each(links, function(self, link){
    self.thenClick(x(link));
    self.waitFor(function check(){
        return resourceAborted;
    });
    self.then(function(){
        resourceAborted = false; // reset state
    });
});

2.2 Single page application

There may be so many event handlers attached, that it is quite hard to prevent them all. An easier way (at least for me) is to

  1. get all the unique element paths,
  2. iterate over the list and do every time the following:
    1. Open the original page again (basically a reset for every link)
    2. do the click on the current XPath

This is basically what I do in this answer.

Since single page apps don't load pages. The navigation.requested and page.resource.requested will not be triggered. You need the resource.requested event if you want to check some API call:

var clickPassed = -1;
casper.on('resource.requested', function(requestData, request){
    if (requestData.url.match(/someURLMatching/)) {
        // you can also check requestData.headers which is an array of objects:
        // [{name: "header name", value: "some value"}]
        clickPassed = true;
    } else {
        clickPassed = false;
    }
});

and in the test flow:

casper.each(links, function(self, link){
    self.thenOpen(startURL);
    self.thenClick(x(link));
    self.waitFor(function check(){
        return clickPassed !== -1;
    }, function then(){
        casper.test.assert(clickPassed);
        clickPassed = -1;
    }, function onTimeout(){
        casper.test.fail("Resource timeout");
    });
});


来源:https://stackoverflow.com/questions/24603365/click-all-anchor-tags-on-page-with-given-class-but-cancel-prior-to-navigation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!