minkphp / MinkZombieDriver

Zombie.js driver for Mink framework
41 stars 49 forks source link

The "wait()" does not work, when Zombie has empty event loop #167

Open Ventzy opened 8 years ago

Ventzy commented 8 years ago

It seems that wait() does not work, or I expect different behavior. I could not find any docs for browser.wait method in Zombie.

So... I expect when I do $session->wait(10000); php script to "sleep" for 10 seconds and then resume. But it does not happen. Am I missing something?

My use case is exactly as comment in Mink wait method - "Waits some time or until JS condition turns true." And I have it like that:

$session->wait(
    1000,
    "document.querySelector('.some-selector') !== null"
);

I want, if .some-selector appears on the page in next 1 second to get true, and if not - get false. But it does not wait a second and returns right away with false.

aik099 commented 8 years ago

Works for me. I'm using following:

And there is a test that proves that it should work.

Ventzy commented 8 years ago

Does not work for me with the same versions. I am using behat, but here I have created Mink only test case - wait-test.zip

After composer install and php test.php I see almost the same microtime() - no wait. My php -v output

PHP 5.5.9-1ubuntu4.14 (cli) (built: Oct 28 2015 01:34:46)
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies
    with Zend OPcache v7.0.3, Copyright (c) 1999-2014, by Zend Technologies
    with Xdebug v2.2.3, Copyright (c) 2002-2013, by Derick Rethans

nodejs -v

v4.3.2

npm -g list

/usr/lib
├── bower@1.7.7
├─┬ npm@3.8.0
│ ├── abbrev@1.0.7
│ ├── ansi-regex@2.0.0
│ ├── ansicolors@0.3.2
│ ├── ansistyles@0.1.3
│ ├── aproba@1.0.1
│ ├── archy@1.0.0
│ ├── async-some@1.0.2
│ ├── chownr@1.0.1
│ ├── cmd-shim@2.0.2
│ ├─┬ columnify@1.5.4
│ │ └─┬ wcwidth@1.0.0
│ │   └─┬ defaults@1.0.3
│ │     └── clone@1.0.2
│ ├─┬ config-chain@1.1.10
│ │ └── proto-list@1.2.4
│ ├── debuglog@1.0.1
│ ├─┬ dezalgo@1.0.3
│ │ └── asap@2.0.3
│ ├── editor@1.0.0
│ ├── fs-vacuum@1.2.7
│ ├── fs-write-stream-atomic@1.0.8
│ ├── fstream@1.0.8
│ ├─┬ fstream-npm@1.0.7
│ │ └─┬ fstream-ignore@1.0.3
│ │   └─┬ minimatch@3.0.0
│ │     └─┬ brace-expansion@1.1.2
│ │       ├── balanced-match@0.3.0
│ │       └── concat-map@0.0.1
│ ├─┬ glob@7.0.0
│ │ ├─┬ minimatch@3.0.0
│ │ │ └─┬ brace-expansion@1.1.3
│ │ │   ├── balanced-match@0.3.0
│ │ │   └── concat-map@0.0.1
│ │ └── path-is-absolute@1.0.0
│ ├── graceful-fs@4.1.3
│ ├── has-unicode@2.0.0
│ ├── hosted-git-info@2.1.4
│ ├── iferr@0.1.5
│ ├── imurmurhash@0.1.4
│ ├── inflight@1.0.4
│ ├── inherits@2.0.1
│ ├── ini@1.3.4
│ ├─┬ init-package-json@1.9.3
│ │ ├─┬ glob@6.0.4
│ │ │ ├─┬ minimatch@3.0.0
│ │ │ │ └─┬ brace-expansion@1.1.3
│ │ │ │   ├── balanced-match@0.3.0
│ │ │ │   └── concat-map@0.0.1
│ │ │ └── path-is-absolute@1.0.0
│ │ └── promzard@0.3.0
│ ├── lockfile@1.0.1
│ ├── lodash._baseindexof@3.1.0
│ ├─┬ lodash._baseuniq@4.5.0
│ │ ├── lodash._createset@4.0.0
│ │ └── lodash._setcache@4.1.0
│ ├── lodash._bindcallback@3.0.1
│ ├── lodash._cacheindexof@3.0.2
│ ├── lodash._createcache@3.1.2
│ ├── lodash._getnative@3.9.1
│ ├─┬ lodash.clonedeep@4.3.0
│ │ └── lodash._baseclone@4.5.0
│ ├── lodash.isarguments@3.0.7
│ ├── lodash.isarray@4.0.0
│ ├── lodash.keys@4.0.4
│ ├── lodash.restparam@3.6.1
│ ├─┬ lodash.union@4.2.0
│ │ ├── lodash._baseflatten@4.1.0
│ │ └── lodash.rest@4.0.1
│ ├── lodash.uniq@4.2.0
│ ├─┬ lodash.without@4.1.0
│ │ ├─┬ lodash._basedifference@4.4.0
│ │ │ └── lodash._setcache@4.1.0
│ │ └── lodash.rest@4.0.1
│ ├─┬ mkdirp@0.5.1
│ │ └── minimist@0.0.8
│ ├─┬ node-gyp@3.3.0
│ │ ├─┬ glob@4.5.3
│ │ │ └─┬ minimatch@2.0.10
│ │ │   └─┬ brace-expansion@1.1.3
│ │ │     ├── balanced-match@0.3.0
│ │ │     └── concat-map@0.0.1
│ │ ├─┬ minimatch@1.0.0
│ │ │ ├── lru-cache@2.7.3
│ │ │ └── sigmund@1.0.1
│ │ └─┬ path-array@1.0.1
│ │   └─┬ array-index@1.0.0
│ │     ├─┬ debug@2.2.0
│ │     │ └── ms@0.7.1
│ │     └─┬ es6-symbol@3.0.2
│ │       ├── d@0.1.1
│ │       └─┬ es5-ext@0.10.11
│ │         └── es6-iterator@2.0.0
│ ├── nopt@3.0.6
│ ├── normalize-git-url@3.0.1
│ ├─┬ normalize-package-data@2.3.5
│ │ └─┬ is-builtin-module@1.0.0
│ │   └── builtin-modules@1.1.1
│ ├── npm-cache-filename@1.0.2
│ ├── npm-install-checks@3.0.0
│ ├── npm-package-arg@4.1.0
│ ├─┬ npm-registry-client@7.1.0
│ │ ├─┬ concat-stream@1.5.1
│ │ │ └── typedarray@0.0.6
│ │ └── retry@0.8.0
│ ├── npm-user-validate@0.1.2
│ ├─┬ npmlog@2.0.2
│ │ ├── ansi@0.3.1
│ │ ├─┬ are-we-there-yet@1.0.6
│ │ │ └── delegates@1.0.0
│ │ └─┬ gauge@1.2.7
│ │   ├─┬ lodash.pad@4.1.0
│ │   │ ├── lodash.repeat@4.0.0
│ │   │ └── lodash.tostring@4.1.1
│ │   ├── lodash.padend@4.2.0
│ │   └── lodash.padstart@4.2.0
│ ├── once@1.3.3
│ ├── opener@1.4.1
│ ├─┬ osenv@0.1.3
│ │ ├── os-homedir@1.0.1
│ │ └── os-tmpdir@1.0.1
│ ├── path-is-inside@1.0.1
│ ├─┬ read@1.0.7
│ │ └── mute-stream@0.0.5
│ ├── read-cmd-shim@1.0.1
│ ├─┬ read-installed@4.0.3
│ │ └── util-extend@1.0.3
│ ├─┬ read-package-json@2.0.3
│ │ ├─┬ glob@6.0.4
│ │ │ ├─┬ minimatch@3.0.0
│ │ │ │ └─┬ brace-expansion@1.1.3
│ │ │ │   ├── balanced-match@0.3.0
│ │ │ │   └── concat-map@0.0.1
│ │ │ └── path-is-absolute@1.0.0
│ │ └─┬ json-parse-helpfulerror@1.0.3
│ │   └── jju@1.2.1
│ ├── read-package-tree@5.1.2
│ ├─┬ readable-stream@2.0.5
│ │ ├── core-util-is@1.0.2
│ │ ├── isarray@0.0.1
│ │ ├── process-nextick-args@1.0.6
│ │ ├── string_decoder@0.10.31
│ │ └── util-deprecate@1.0.2
│ ├── readdir-scoped-modules@1.0.2
│ ├── realize-package-specifier@3.0.1
│ ├─┬ request@2.69.0
│ │ ├── aws-sign2@0.6.0
│ │ ├─┬ aws4@1.2.1
│ │ │ └── lru-cache@2.7.3
│ │ ├── bl@1.0.1
│ │ ├── caseless@0.11.0
│ │ ├─┬ combined-stream@1.0.5
│ │ │ └── delayed-stream@1.0.0
│ │ ├── extend@3.0.0
│ │ ├── forever-agent@0.6.1
│ │ ├─┬ form-data@1.0.0-rc3
│ │ │ └── async@1.5.2
│ │ ├─┬ har-validator@2.0.6
│ │ │ ├─┬ chalk@1.1.1
│ │ │ │ ├── ansi-styles@2.1.0
│ │ │ │ ├── escape-string-regexp@1.0.4
│ │ │ │ ├── has-ansi@2.0.0
│ │ │ │ └── supports-color@2.0.0
│ │ │ ├─┬ commander@2.9.0
│ │ │ │ └── graceful-readlink@1.0.1
│ │ │ ├─┬ is-my-json-valid@2.12.4
│ │ │ │ ├── generate-function@2.0.0
│ │ │ │ ├─┬ generate-object-property@1.2.0
│ │ │ │ │ └── is-property@1.0.2
│ │ │ │ ├── jsonpointer@2.0.0
│ │ │ │ └── xtend@4.0.1
│ │ │ └─┬ pinkie-promise@2.0.0
│ │ │   └── pinkie@2.0.1
│ │ ├─┬ hawk@3.1.3
│ │ │ ├── boom@2.10.1
│ │ │ ├── cryptiles@2.0.5
│ │ │ ├── hoek@2.16.3
│ │ │ └── sntp@1.0.9
│ │ ├─┬ http-signature@1.1.1
│ │ │ ├── assert-plus@0.2.0
│ │ │ ├─┬ jsprim@1.2.2
│ │ │ │ ├── extsprintf@1.0.2
│ │ │ │ ├── json-schema@0.2.2
│ │ │ │ └── verror@1.3.6
│ │ │ └─┬ sshpk@1.7.3
│ │ │   ├── asn1@0.2.3
│ │ │   ├── dashdash@1.12.2
│ │ │   ├── ecc-jsbn@0.1.1
│ │ │   ├── jodid25519@1.0.2
│ │ │   ├── jsbn@0.1.0
│ │ │   └── tweetnacl@0.13.3
│ │ ├── is-typedarray@1.0.0
│ │ ├── isstream@0.1.2
│ │ ├── json-stringify-safe@5.0.1
│ │ ├─┬ mime-types@2.1.9
│ │ │ └── mime-db@1.21.0
│ │ ├── node-uuid@1.4.7
│ │ ├── oauth-sign@0.8.0
│ │ ├── qs@6.0.2
│ │ ├── stringstream@0.0.5
│ │ ├── tough-cookie@2.2.1
│ │ └── tunnel-agent@0.4.2
│ ├── retry@0.9.0
│ ├── rimraf@2.5.2
│ ├── semver@5.1.0
│ ├── sha@2.0.1
│ ├── slide@1.1.6
│ ├── sorted-object@1.0.0
│ ├── strip-ansi@3.0.1
│ ├─┬ tar@2.2.1
│ │ └── block-stream@0.0.8
│ ├── text-table@0.2.0
│ ├── uid-number@0.0.6
│ ├── umask@1.1.0
│ ├─┬ unique-filename@1.1.0
│ │ └── unique-slug@2.0.0
│ ├── unpipe@1.0.0
│ ├─┬ validate-npm-package-license@3.0.1
│ │ ├─┬ spdx-correct@1.0.2
│ │ │ └── spdx-license-ids@1.2.0
│ │ └─┬ spdx-expression-parse@1.0.2
│ │   ├── spdx-exceptions@1.0.4
│ │   └── spdx-license-ids@1.2.0
│ ├─┬ validate-npm-package-name@2.2.2
│ │ └── builtins@0.0.7
│ ├─┬ which@1.2.4
│ │ ├─┬ is-absolute@0.1.7
│ │ │ └── is-relative@0.1.3
│ │ └── isexe@1.1.1
│ ├── wrappy@1.0.1
│ └── write-file-atomic@1.1.4
└─┬ zombie@4.2.1
  ├─┬ babel-runtime@5.8.29
  │ └── core-js@1.2.6
  ├── bluebird@3.3.3
  ├── debug@2.2.0
  ├─┬ eventsource@0.1.6
  │ └─┬ original@1.0.0
  │   └─┬ url-parse@1.0.5
  │     ├── querystringify@0.0.3
  │     └── requires-port@1.0.0
  ├── iconv-lite@0.4.13
  ├─┬ jsdom@5.3.0
  │ ├── acorn@1.2.2
  │ ├─┬ acorn-globals@1.0.9
  │ │ └── acorn@2.7.0
  │ ├── browser-request@0.3.3
  │ ├── cssom@0.3.1
  │ ├── cssstyle@0.2.34
  │ ├─┬ escodegen@1.8.0
  │ │ ├── esprima@2.7.2
  │ │ ├── estraverse@1.9.3
  │ │ ├── esutils@2.0.2
  │ │ ├─┬ optionator@0.8.1
  │ │ │ ├── deep-is@0.1.3
  │ │ │ ├── fast-levenshtein@1.1.3
  │ │ │ ├── levn@0.3.0
  │ │ │ ├── prelude-ls@1.1.2
  │ │ │ ├── type-check@0.3.2
  │ │ │ └── wordwrap@1.0.0
  │ │ └─┬ source-map@0.2.0
  │ │   └── amdefine@1.0.0
  │ ├─┬ htmlparser2@3.9.0
  │ │ ├── domelementtype@1.3.0
  │ │ ├── domhandler@2.3.0
  │ │ ├─┬ domutils@1.5.1
  │ │ │ └─┬ dom-serializer@0.1.0
  │ │ │   └── domelementtype@1.1.3
  │ │ ├── entities@1.1.1
  │ │ └─┬ readable-stream@2.0.5
  │ │   ├── core-util-is@1.0.2
  │ │   ├── inherits@2.0.1
  │ │   ├── isarray@0.0.1
  │ │   ├── process-nextick-args@1.0.6
  │ │   ├── string_decoder@0.10.31
  │ │   └── util-deprecate@1.0.2
  │ ├── nwmatcher@1.3.7
  │ ├── parse5@1.5.1
  │ ├─┬ tough-cookie@0.13.0
  │ │ └── punycode@1.4.0
  │ ├── xml-name-validator@2.0.1
  │ └── xmlhttprequest@1.8.0
  ├── lodash@3.10.1
  ├── mime@1.3.4
  ├── ms@0.7.1
  ├─┬ request@2.69.0
  │ ├── aws-sign2@0.6.0
  │ ├─┬ aws4@1.3.2
  │ │ └─┬ lru-cache@4.0.0
  │ │   ├── pseudomap@1.0.2
  │ │   └── yallist@2.0.0
  │ ├── bl@1.0.3
  │ ├── caseless@0.11.0
  │ ├─┬ combined-stream@1.0.5
  │ │ └── delayed-stream@1.0.0
  │ ├── extend@3.0.0
  │ ├── forever-agent@0.6.1
  │ ├─┬ form-data@1.0.0-rc3
  │ │ └── async@1.5.2
  │ ├─┬ har-validator@2.0.6
  │ │ ├─┬ chalk@1.1.1
  │ │ │ ├─┬ ansi-styles@2.2.0
  │ │ │ │ └── color-convert@1.0.0
  │ │ │ ├── escape-string-regexp@1.0.5
  │ │ │ ├─┬ has-ansi@2.0.0
  │ │ │ │ └── ansi-regex@2.0.0
  │ │ │ ├── strip-ansi@3.0.1
  │ │ │ └── supports-color@2.0.0
  │ │ ├─┬ commander@2.9.0
  │ │ │ └── graceful-readlink@1.0.1
  │ │ ├─┬ is-my-json-valid@2.13.1
  │ │ │ ├── generate-function@2.0.0
  │ │ │ ├─┬ generate-object-property@1.2.0
  │ │ │ │ └── is-property@1.0.2
  │ │ │ ├── jsonpointer@2.0.0
  │ │ │ └── xtend@4.0.1
  │ │ └─┬ pinkie-promise@2.0.0
  │ │   └── pinkie@2.0.4
  │ ├─┬ hawk@3.1.3
  │ │ ├── boom@2.10.1
  │ │ ├── cryptiles@2.0.5
  │ │ ├── hoek@2.16.3
  │ │ └── sntp@1.0.9
  │ ├─┬ http-signature@1.1.1
  │ │ ├── assert-plus@0.2.0
  │ │ ├─┬ jsprim@1.2.2
  │ │ │ ├── extsprintf@1.0.2
  │ │ │ ├── json-schema@0.2.2
  │ │ │ └── verror@1.3.6
  │ │ └─┬ sshpk@1.7.4
  │ │   ├── asn1@0.2.3
  │ │   ├─┬ dashdash@1.13.0
  │ │   │ └── assert-plus@1.0.0
  │ │   ├── ecc-jsbn@0.1.1
  │ │   ├── jodid25519@1.0.2
  │ │   ├── jsbn@0.1.0
  │ │   └── tweetnacl@0.14.1
  │ ├── is-typedarray@1.0.0
  │ ├── isstream@0.1.2
  │ ├── json-stringify-safe@5.0.1
  │ ├─┬ mime-types@2.1.10
  │ │ └── mime-db@1.22.0
  │ ├── node-uuid@1.4.7
  │ ├── oauth-sign@0.8.1
  │ ├── qs@6.0.2
  │ ├── stringstream@0.0.5
  │ └── tunnel-agent@0.4.2
  ├── tough-cookie@2.2.1
  └─┬ ws@0.8.1
    ├─┬ bufferutil@1.2.1
    │ ├── bindings@1.2.1
    │ └── nan@2.2.0
    ├── options@0.0.6
    ├── ultron@1.0.2
    └── utf-8-validate@1.2.1

Env: Ubuntu Server 14.04 in VirtualBox

aik099 commented 8 years ago

I've constructed NodeJS code (Zombie only, no Mink) example that is executed from your example:

var zombie = require('zombie'),
    browser = new zombie();

var checkCondition = function () {
  return browser.evaluate('false');
};

browser.visit('http://www.google.com');

var $start = (new Date()).getTime();

browser.wait({function: checkCondition, duration: 5000}, function () {
  console.log('condition at the end of wait: ', checkCondition());
});

var $duration = (new Date()).getTime() - $start;

console.log('duration: ', $duration, 's');

No matter what duration (in place of 5000) I specify waiting takes 1 or 2 seconds at random. The wait function in Zombie doesn't really tell that it will wait for element to appear (or any constant given time) before returning: https://github.com/assaf/zombie/blob/master/src/index.js#L242

I've created an issue for Zombie about this: https://github.com/assaf/zombie/issues/1032

aik099 commented 8 years ago

According to https://github.com/assaf/zombie/issues/696 the wait doesn't really ensure the wait functionality we need.

@stof , I'm proposing to add tests, that assert that waiting actually performed given amount of seconds and rewrite waiting implementation in this driver to work as in Selenium2 (just PHP cycle with usleep and JS call inside it).

Ventzy commented 8 years ago

Here is solution in JS. I haven't tested it too much, but it seems to be working for me. The interval of execution of condition check is hardcoded 100ms, but it should be parameter of the wait function.

        $js = <<<JS
(function () {
    var checkCondition, firstCheckResult, timerId, intervalId;

    checkCondition = function () {
      return browser.evaluate($conditionEscaped);
    };

    firstCheckResult = checkCondition();
    if (firstCheckResult !== false) {
        stream.end(JSON.stringify(firstCheckResult));
    } else {
        timerId = setTimeout(function() {
            clearTimeout(timerId);
            clearInterval(intervalId);

            var lastCheckResult = checkCondition();
            if (lastCheckResult !== false) {
                stream.end(JSON.stringify(firstCheckResult));
            } else {
                stream.end('false');
            }
        }, $timeout);

        intervalId = setInterval(function() {
            var intervalCheckResult = checkCondition();
            if (intervalCheckResult !== false) {
                clearTimeout(timerId);
                clearInterval(intervalId);

                stream.end(JSON.stringify(intervalCheckResult));
            }
        }, 100);
    }
}());
JS;
aik099 commented 8 years ago

@Ventzy , I somehow missed your comment back then.

Please send a PR with proposed solution. Not sure why you need to use both interval and timer at the same time. I suggest doing this:

This is better, because setInterval don't care if processing once interval is hit takes longer than interval length and you might end up running 2 element existence checks in parallel.