segment-boneyard / nightmare

A high-level browser automation library.
https://open.segment.com
19.55k stars 1.08k forks source link

Custom action dont works #959

Closed necromind closed 7 years ago

necromind commented 7 years ago

Code from here: https://github.com/segmentio/nightmare/issues/887#issuecomment-269412280

Nightmare.action('sendKeyPress',
  function(name, options, parent, win, renderer, done) {
    parent.respondTo('sendKeyPress', function(unicodeValue, done) {
      win.focus();
      win.webContents.sendInputEvent({
        type: "keyDown",
        keyCode: unicodeValue,
      });
      setTimeout(() => {
        win.webContents.sendInputEvent({
          type: "keyUp",
          keyCode: unicodeValue,
        });
        done();
      },50);
    });
    done();
  },
  function(unicodeValue, done) {
    this.child.call('sendKeyPress', unicodeValue, done);
  });

    nightmare
        //.cookies.clearAll()
        .viewport(1280, 1024)
        .goto('https://url/')
        .sendKeyPress('\u202D')
        .run();

And i have error:

return fn.apply(self, doneargs);
             ^
TypeError: Cannot read property 'apply' of undefined

Why dont works? This is like example from main wiki "Defining a custom action with an Electron method"

necromind commented 7 years ago

Solved. done must be first:

  function(done, unicodeValue) {
    this.child.call('sendKeyPress', unicodeValue, done);
  });

But this code anyway dont send press right key event.

necromind commented 7 years ago
var Nightmare = require('nightmare');

Nightmare.action('clearCache',
  function(name, options, parent, win, renderer, done) {
    parent.respondTo('clearCache', function(done) {
      //win.webContents.session.clearCache(done);
    });
    done();
  },
  function(message,done) {
    this.child.call('clearCache', done);
  });

  var  nightmare = Nightmare(
      {
        show: true
      });

  nightmare
    //go to a url
    .goto('ulr')
    //clear the cache
    .clearCache()
    .wait(3000)
    //go to another url
    .goto('http://ya.ru')
    //perform other actions
    .then(() => {
      //...
    })
    //... other actions...
    //.then(nightmare.end())
    .catch((e) => console.dir(e));

This code from examples dont work too! App sleeps at clearCache point. HOW can i get access to win.webContents for using sendInputEvent. Help me pls.

rosshinkley commented 7 years ago

Your clearCache method takes a message parameter, but you're not passing anything to the call in your Nightmare chain. What happens if you try changing function(message, done) to just function(done)?

gneccao commented 7 years ago

i have the same problem with the custom actions. I'm using the 2.9.1 and still have the problem for me. I try to use the example in the home page, but appears a error to me: "Unhandled promise rejection (rejection id: 1): Nothing responds to "clearCache".

contobob commented 7 years ago

+1. No custom action works for me either if it involves interacting with electron.

I can't even get the clearCache custom action from the documentation to work.

Here's a test script (copy-pasted clearCache from doc):

var Nightmare = require('nightmare');
Nightmare.action('clearCache',
    function(name, options, parent, win, renderer, done) {
        parent.respondTo('clearCache', function(done) {
            win.webContents.session.clearCache(done);
        });
        done();
    },
    function(done) {
        this.child.call('clearCache', done);
    });

var nightmare = Nightmare({
    show: true,
    electronPath: require('electron'),
});

nightmare
    .clearCache()
    .goto('http://google.com')
    .evaluate(function () {
        return document.title;
    })
    .end()
    .then(function (result) {
        console.log(result);
    })
    .catch(function (error) {
        console.error('Retrieve title failed:', error);
    });

What happens when I run node actionstest.js: a blank electron window opens up. Nightmare then stalls on the white page until I quit the script via ctrl-c.

Full output for DEBUG=nightmare:*,electron:* node actionstest.js (it stalls immediately after):

  nightmare:log adding action for clearCache +0ms

Nightmare verision 2.9.1. Electron version 1.5.0. Unlikely related but: macOS Sierra 10.12.2.

Any help here would be greatly appreciated.

The bug, whatever it is, renders all custom actions useless, including kyungw00k/nightmare-webrequest-addon and rosshinkley/nightmare-load-filter.

rosshinkley commented 7 years ago

@contobob What you have there will almost certainly not work: win.webContents.session does not exist until you navigate away from default:blank. (Why would you try to clear a session cache before it exists?)

If you're having problems with those plugins, i would encourage you to open issues there.

contobob commented 7 years ago

Thanks for the reply @rosshinkley. Does that mean the docs in README.md should be updated? My test code was mainly a copy-paste of what can be found here in the clearCache example. The doc puts clearCache before goto.

ysimonson commented 7 years ago

Having the same issue. This is almost a copy-paste from the readme:

Nightmare.action('clearCache',
  function(name, options, parent, win, renderer, done) {
    parent.respondTo('clearCache', function(done) {
      win.webContents.session.clearCache(done);
    });
    done();
  },
  function(done) {
    this.child.call('clearCache', done);
  });

var nightmare = Nightmare({
    show: true,
    webPreferences: {
        partition: 'testaction'
    }
});

nightmare
  .clearCache()
  .goto('http://example.org')
  .then(function() {
    console.log("done");
    process.exit(0);
  });

However, it works fine if you remove the bits that call/declare the action:

var nightmare = Nightmare({
    show: true,
    webPreferences: {
        partition: 'testaction'
    }
});

nightmare
  .goto('http://example.org')
  .then(function() {
    console.log("done");
    process.exit(0);
  });
ysimonson commented 7 years ago

It's not an issue strictly with the placement of clearCache - granted that's probably wrong, even though it's in the readme. If you call goto before clearCache, it'll never get past clearCache - i.e. in this example, it'll print A, but not B or C.

Nightmare.action('clearCache',
  function(name, options, parent, win, renderer, done) {
    console.log("B");
    parent.respondTo('clearCache', function(done) {
      console.log("C");
      win.webContents.session.clearCache(done);
    });
    done();
  },
  function(done) {
    console.log("A");
    this.child.call('clearCache', done);
  });

var nightmare = Nightmare({
    show: true,
    webPreferences: {
        partition: 'testaction'
    }
});

nightmare
  .goto('http://example.org')
  .clearCache()
  .then(function() {
    console.log("done");
    process.exit(0);
  });

IIRC, I saw the same issue w/ @rosshinkley's nightmare-upload. FWIW, on nightmare 2.9.1 as well.

rosshinkley commented 7 years ago

@ysimonson It does print B and C, just not to stdout of the node process. Instead, those get printed (rightfully) to Electron's stdout. Consider your above script, but instead of running with node example.js you run with DEBUG=electron* node example.js. You'll see the following output:

  electron:stdout B +0ms
A
  electron:stdout C +358ms

... which is precisely what you would expect to see. B when the action is created on the instance, followed by A when the action is called, followed by C when the action responds to the call.

rosshinkley commented 7 years ago

Bah, I realized I didn't answer the question of why it's not calling back: It looks like the clearCache callback isn't getting called. I'll dig into that.

entrptaher commented 7 years ago

@rosshinkley Have a look into this, its been several months since we've found such issue. Nothing responds to such actions. :/

screenshot from 2017-03-12 22-59-35

rosshinkley commented 7 years ago

In this particular case, it looks like webContents.session.clearCache never calls back, and it's not immediately clear why. I'm inclined to say this is a bug with Electron: the source for clearCache does not have a callback call, at least in my cursory look. For contrast, compare that to clearStorage that does.

If you move the done call to the line after clearCache, does the cache actually clear? This may be worth opening with Electron. Thoughts?

aight8 commented 7 years ago

Please fix. Is this package deprecated?

entrptaher commented 7 years ago

@rosshinkley If you create custom actions, almost same kind of error happens no matter the callback.

aight8 commented 7 years ago

IMPORTANT I hope I can help - spend some hours on it now.

To this package to improve:

  var name = arguments[0], childfn, parentfn;
  if(arguments.length === 2) {
    parentfn = arguments[1];
  } else {
    parentfn = arguments[2];
    childfn = arguments[1];
  }
entrptaher commented 7 years ago

Custom actions worked if I used it after a goto, but sometimes you need actions to do before going to any url. But what more important is that all examples from the docs are not working properly. Any action after goto works and any action before goto simply fails.

aight8 commented 7 years ago

Yes I know they should be removed all, temporarily. Currently not examples but broken code.

entrptaher commented 7 years ago

This is what I had to do with all done() functions to make it working. Example code: https://gist.github.com/entrptaher/870246d824c10317edf72a0d4b86760e

done inside parent, done inside the action and also done in the callbacks. It's a done-strophic problem :D

rosshinkley commented 7 years ago

Custom actions worked if I used it after a goto, but sometimes you need actions to do before going to any url.

I think I understand what the problem is now after seeing your example. When Nightmare.action() (on the prototype) is called, there's an action put on the prototypal queue to execute action methods on all created instances. Looking at your gist, it occurs to me there might be a bug when immediately calling a plugin method. Poking around in the unit tests for Nightmare, I don't think this particular case is tested for and is probably worth adding to at least prove out that's not the problem.

Exceptions on electron side should be catched and redirected to the client and throwed there again with a note that it happened on electron side. Currently it crashes without infos.

This is a somewhat complicated topic. See #646.

...and the optional value should be at the end - I can imagine the reason was the order, whyever...

This is because .action() already existed, and I wanted to have as much backwards compatibility with previous versions as possible.

The API documentation has following flaws:

You missed a few. ;) Kidding aside, this has been on my backburner for a long time - it's why nightmare-examples exists. I'd like for this to all be wrapped up on github pages, but I haven't made it that far yet.

I saw that @types/nightmare is currently on version 1.6, older than 2 years.

PR DefinitelyTyped?

If you create custom actions, almost same kind of error happens no matter the callback.

I don't think that's true as there are a lot of plugin libraries that use this. That said, I'm almost positive you've hit a bug with either how caching works in Electron and/or with how actions are created on the instance after Electron is started.

SkyHacks commented 7 years ago

I ran into issues when my custom Electron actions were defined after the instance was created. Moving those up fixed the issues I was having. Just FYI to anyone who makes the same mistake.

casesandberg commented 7 years ago

Looks like this is resolved. There are a couple of points of things to improve but those should be opened as separate issues. I am going to close this for now but feel free to open a new issue if you are still having problems.

rinogo commented 5 years ago

As @entrptaher mentioned, since the clearCache callback isn't getting called, "manually" calling done() seems to fix the problem. However, couldn't that potentially mess up timing for subsequent requests?

On a "fast" machine, it seems to me that some of the first subsequent HTTP requests would still use the cache, and then once clearCache had actually cleared the cache (at some non-deterministic time), HTTP requests would actually be performed (i.e. actual packets over the network) since the cache would finally be empty. Right? Or am I misunderstanding something?