1

I have this drop down list:

<select name="date" class="form-control" on-change:Calendar.submitFilterForm();">
    <option value="2015-09-15">Tue 2015-09-15</option>
    <option value="2015-09-16">Wed 2015-09-16</option>
    <option value="2015-09-17">Thu 2015-09-17</option> 
    <option value="2015-09-18">Fri 2015-09-18</option>
    <option value="2015-09-19">Sat 2015-09-19</option>
    <option value="2015-09-20">Sun 2015-09-20</option>
    <option value="2015-09-21">Mon 2015-09-21</option>
    <option value="2015-09-22">Tue 2015-09-22</option>
</select>

Each night at 00:00 AM (or a few seconds later) the drop-down list above is updated with a new (next) day, for example <option value="2015-09-23">Wed 2015-09-23</option> would be added and <option value="2015-09-15">Tue 2015-09-15</option> would disappear.

I then want to click on that drop-down list option that is added as soon as it is visible. Is it possible?

Now I use:

casper.thenEvaluate(function(){
    var form = document.querySelector('.form-control');
    form.selectedIndex = 7; //7 equals the last value currently visible.
    $(form).change();
});

It works but how should I do to make casperjs wait until the options is visible and then directly click on it? Maybe I can make a variable var ClickValue = "2015-09-23" or something like that?

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
user1665355
  • 3,324
  • 8
  • 44
  • 84

1 Answers1

2

CasperJS is not the right place to schedule long running tasks, because the underlying browser (PhantomJS or Slimer.js) may run into memory problems or otherwise fail. So you should write a short running script that only waits a short amount of time until the element appears. You can schedule the execution of this script just before midnight through your operating system (cron on Linux or at on Windows).

Element appears through JavaScript

If the select field is updated by JavaScript, then you can simply use a "long"-running waitFor():

casper.then(function(){
    var lastDate = this.getElementAttribute(".form-control :last-child", "value");
    this.waitFor(function(){
        return this.getElementAttribute(".form-control :last-child", "value") !== lastDate;
    }, null, null, 600000); // 10 minutes
}).thenEvaluate(function(){
    var form = document.querySelector('.form-control');
    form.selectedIndex = 7; //7 equals the last value currently visible.
    $(form).change();
});

A page reload is necessary

If you will see the added option only when you reload the page, then you need casper.reload() the page for every check.

casper.then(function(){
    var lastDate = this.getElementAttribute(".form-control :last-child", "value");

    function checkReload(){
        var curDate = this.getElementAttribute(".form-control :last-child", "value");
        if (lastDate !== curDate) { // TODO: add timeout handling here
            return; // finished
        }

        this.reload();
        this.wait(1000, checkReload); // check again in a second
    }
    this.then(checkReload);
}).thenEvaluate(function(){
    var form = document.querySelector('.form-control');
    form.selectedIndex = 7; //7 equals the last value currently visible.
    $(form).change();
});

This runs indefinitely, so you should add timeout handling.

No comparison necessary with precomputed date

The above solutions work by comparing the previous option value with the current one. This is not entirely robust, because if something wrong happens and the script is started at a time when the new option appears, then the script will run another 24 hours. You could also change that to directly compute the expected option value by creating the expected

var d = new Date(Date.now()+(1000 * 60 * 60 * 24 * 7)); // seven days later
var expectedValue = ""+d.getFullYear()
        +"-"+zeroFill(d.getMonth()+1, 2)
        +"-"+zeroFill(d.getDate(), 2);

expectedValue would replace lastDate in the previous snippets and don't forget to flip the comparison from !== to ===.
zeroFill() is taken from here.

Community
  • 1
  • 1
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
  • Great answer:) So I should use `zeroFill()` (variable `expectedValue`( instead of `curDate `? – user1665355 Sep 15 '15 at 11:35
  • No, `expectedValue` would replace `lastDate`. The comparison also needs to be flipped – Artjom B. Sep 15 '15 at 11:38
  • Ok! But now when I try this script it simply ends. I mean `lastDate ` and `curDate ` is the same when I test it now, so the script should just do `.wait(1000, checkReload);` ? Or? – user1665355 Sep 15 '15 at 11:42
  • It maybe should say `this.wait(1000, checkReload);` ? – user1665355 Sep 15 '15 at 11:48
  • I'm not sure what the issue is there. Can you print the contents of lastDate and curDate? This may be a PhantomJS bug at work. – Artjom B. Sep 15 '15 at 12:06
  • You mean `this.reload(); this.wait(...);`? That shouldn't make a difference. – Artjom B. Sep 15 '15 at 12:23
  • Can I ask you one more question? It seems that `return` in `checkReload` function starts the next step, `thenEvaluate` before the page is loaded. It seems that the drop-down list new element (`curDate`) is present before I can click it... Any suggestion about what I can do? Maybe some `wait` function again? – user1665355 Sep 18 '15 at 07:04
  • You mean the first invocation of checkReload? Yes, you should waitForSelector of the select box. – Artjom B. Sep 18 '15 at 07:41
  • I mean that when condition `lastDate !== curDate` is `TRUE` then the code proceeds to `thenEvaluate`. But I see in my `casper.capture` later in the code that the page is not really loaded when I proceed to `thenEvaluate`... So the drop-down list is updated before the page is loaded? – user1665355 Sep 18 '15 at 07:47
  • Can I ask you in a chat? – user1665355 Sep 18 '15 at 08:33
  • This means that the whole code isn't working. The second snippet is supposed to work when the select box is fully loaded on page load or at the latest a second after that. You could increase the checking interval. Maybe there is a general problem with PhantomJS and that selectbox, but I don't see how, because it worked for you with the code in the question. You could try the more robust version in the last part of the answer if you know/can calculate the value beforehand. (Note: you don't have to immediately accept an answer when you get one) – Artjom B. Sep 18 '15 at 11:21
  • See my edit, I get an error: `undefined is not a constructor (evaluating this.then(checkPage)`. Is it possible to do like that? – user1665355 Sep 18 '15 at 11:28
  • I don't see how that is possible. Can you create a (secret) [gist](https://gist.github.com/) with your current code? – Artjom B. Sep 18 '15 at 11:31
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/90020/discussion-between-user1665355-and-artjom-b). – user1665355 Sep 18 '15 at 11:32
  • I wrote in a chat, hope it is ok. – user1665355 Sep 18 '15 at 11:36