assaf / zombie

Insanely fast, full-stack, headless browser testing using node.js
http://zombie.js.org/
MIT License
5.65k stars 518 forks source link

onchange event submitting a form results in endless loop #1151

Open TheFive opened 6 years ago

TheFive commented 6 years ago

I have a short sample, where a standard browser can react on the onchange event of a text field, but zombie.js runs in an infinite loop.

var Browser = require("zombie");

// create a small test app increasing a counter
var express = require('express');
var app = express();
let counter = 0;

app.post('/increasecounter',function(req,res){
  console.log("Increasecounter: called");
  counter = counter +1;
  let referer = req.header("Referer") || "/noref";
  console.log("Increasecounter: referer "+referer);
  res.redirect(referer);
});

app.get('/',function(req,res){
  console.log("Homeroute: "+counter);
  let page = `
<html>
<body>
<span>${counter}</span>

<form id="form" action="/increasecounter" method="post">
  <input onchange="document.getElementById('form').submit()" type="text" name="counter" value="${counter+1}">
  <br>
  <input id="OK" type="submit" value="Submit">
</form> 

</body>
</html>
`;

  res.send(page);
});

app.listen(3001, function () {
  console.log('Example app listening on port 3001!');
});

Browser.localhost('example.com', 3001);

async function testCallbackAsync() {

  // init browser
  let b = new Browser();

  // visit Homepage and check counter
  console.log("Visiting /")
  await b.visit("/");
  b.assert.text("span","0");

  // submit form with input button and check counter
  console.log("click now");
  await b.click("input#OK");
  b.assert.text("span","1");

  // change input field and trigger onchange event
  console.log("fill textfield");
  b.fill("counter","text");

  //--->> This assertion is not reached, use DEBUG=zombie when run.
  b.assert.text("span","2");
}
testCallbackAsync();

The file runs "on its own" but please start with DEBUG=zombie.

Visiting /
  zombie Opened window http://example.com/  +0ms
Example app listening on port 3001!
Homeroute: 0
  zombie GET http://example.com/ => 200 +42ms
  zombie Loaded document http://example.com/ +21ms
click now
  zombie Event loop is empty +7ms
  zombie Submit form to http://example.com/increasecounter +3ms
  zombie Opened window http://example.com/increasecounter  +2ms
Increasecounter: called
Increasecounter: referer http://example.com/
  zombie POST http://example.com/increasecounter => 302 http://example.com/ +10ms
Homeroute: 1
fill textfield
  zombie GET http://example.com/ => 200 +4ms
  zombie Loaded document http://example.com/ +3ms
  zombie Event loop is empty +0ms
  zombie Submit form to http://example.com/increasecounter +3ms
  zombie Opened window http://example.com/increasecounter  +2ms
  zombie Submit form to http://example.com/increasecounter +1ms
  zombie Opened window http://example.com/increasecounter  +1ms
  zombie Submit form to http://example.com/increasecounter +1ms
  zombie Opened window http://example.com/increasecounter  +2ms

You can see, that clicking the OK button calls the POST route, but triggering the onchange event in the textfield results in an endless loop.

If you call the server after the test with a normal browser, changing the textfield e.g. to 'test' does increase the counter as expected.

TheFive commented 6 years ago

If I replace

 b.fill("counter","text");

by

b.query("input[name='counter']").value = "text";
await b.fire("input[name='counter']","change")

It works.