rosshinkley / nightmare-upload

34 stars 7 forks source link

having <input type="file" inside <iframe not working #9

Open typekpb opened 7 years ago

typekpb commented 7 years ago

Once testing on iframed input I get error:

 1) Nightmare Upload upload should upload a single file using iframe:
     Error: done() invoked with non-Error: {"code":-32000,"message":"Could not find node with given id"}
      at process._tickCallback (internal/process/next_tick.js:109:7)

I've introduced the iframe-based test:

added to package.json

"devDependencies": {
    ...
    "nightmare-iframe-manager": "0.0.4"
}

added to test/index.js

    it('should upload a single file using iframe', function*() {
        require('nightmare-iframe-manager')(Nightmare);
        var nightmare = new Nightmare();
        var files = yield nightmare.goto(fixture('upload_iframe'))
            .enterIFrame('#iframe')
            .upload('input[type=file]', path.resolve(__dirname, 'files', 'upload.txt'))
            .click('button[type=submit]')
            .wait(1000)
            .evaluate(function() {
                return JSON.parse(document.body.querySelector('pre')
                    .innerHTML)
            });
        files[0].originalname.should.equal('upload.txt');
        yield nightmare.end();
    });

introduced new file test/fixtures/upload_iframe/index.html:

<!DOCTYPE html>
<html>

<head>
    <title>Uploade</title>
</head>

<body>
    <iframe src="../upload/index.html" id="iframe"></iframe>
</body>

</html>

afterwards ran: npm instlall && npm test

Is there any proper way to have this working? Or is there a fix in plugin needed?

ysd123 commented 7 years ago

i write as example but error ,show my code next:

image imgFile is not null but upload return an error: { code: -32000, message: 'Could not find node with given id' }

then i change selector 2 ("#"+fileId) still this error

rodrigoruiz commented 6 years ago

Did anyone solve this problema?

LaysDragon commented 5 years ago

Since this plugin is base on devtools protocol,so iframe plugin won't work here. After whole night experiment on Chrome Devtools Protocol. I found a way to solve this problem. I have no idea why we cannot query element in iframe,since it looks like elements in iframe only have the nodeid 0 that wasn't usable to DOM.setFileInputFiles. Only way to get it working is get iframe's node object via DOM.describeNode with parameters:{pierce: true, depth: -1} to get the whole sub tree inside iframe,and find your target element manually. Then use its backendNodeId on DOM.setFileInputFiles. And this mean I need to implement a custom simple selector function instead of use the native selector feature :/. here is part of the code that I try to let it work on my target website.

parent.emit('log', 'paths', pathsToUpload)
try {
  //attach the debugger
  //NOTE: this will fail if devtools is open
  win.webContents.debugger.attach('1.3')
} catch (e) {
  parent.emit('log', 'problem attaching', e)
  return done(e)
}

win.webContents.debugger.sendCommand('DOM.getDocument', {
  pierce: true,
  integer: -1
}, function (err, domDocument) {
  parent.emit('log', 'getDocument', JSON.stringify(domDocument))
  win.webContents.debugger.sendCommand('DOM.querySelector', {
    nodeId: domDocument.root.nodeId,
    selector: '#photoUploadDialog'
  }, function (err, iframequeryResult) {
    parent.emit('log', 'querySelector #photoUploadDialog', JSON.stringify(iframequeryResult))
    win.webContents.debugger.sendCommand('DOM.describeNode', {
      nodeId: iframequeryResult.nodeId,
      pierce: true,
      depth: -1
    }, function (err, queryResult) {
      parent.emit('log', 'describeNode #photoUploadDialog', JSON.stringify(queryResult))

      function arrayContainsArray(superset, subset) {
        if (0 === subset.length || superset.length < subset.length) {
          return false
        }
        for (var i = 0; i < subset.length; i++) {
          if (superset.indexOf(subset[i]) === -1) return false
        }
        return true
      }

      function findChildWithAttributes(node, name, attributes) {
        // parent.emit('log', 'findChildWithAttributes', JSON.stringify(node),name,attributes)
        if (node.nodeName && node.nodeName === name && node.attributes && arrayContainsArray(node.attributes, attributes)) {
          return node
        }
        if (!node.children) return null
        for (let child of node.children) {
          let node = findChildWithAttributes(child, name, attributes)
          if (node !== null) {
            return node
          }
        }
        return null
      }

      let fileNodes = findChildWithAttributes(JSON.parse(JSON.stringify(queryResult.node.contentDocument)), 'INPUT', ['class', 'j-input-add', 'type', 'file'])
      parent.emit('log', 'fileNodes', JSON.stringify(fileNodes))

      // parent.emit('log', 'querySelector', selector, queryResult)
      win.webContents.debugger.sendCommand('DOM.setFileInputFiles', {
        backendNodeId: fileNodes.backendNodeId,
        files: pathsToUpload
      }, function (err, setFileResult) {
        if (Object.keys(err)
           .length > 0) {
          parent.emit('log', 'problem setting input', err)
          return done(err)
        }
        win.webContents.debugger.detach()
        done(null, pathsToUpload)
      })

    })
  })
})
LaysDragon commented 5 years ago

I have make a simple implementation here. https://github.com/LaysDragon/nightmare-upload/commit/1936db41bf1467e69bff6a8e65907a47dddcff96

OuZuYu commented 5 years ago

I have make a simple implementation here. LaysDragon@1936db4

@LaysDragon Hello, If the input does not have an id or class, this method cannot work.

LaysDragon commented 5 years ago

, If the input does not have an id or class, this method cannot work.

I have no Idea how to locate the file input that don't have the id or class,since it really a ugly way to upload the file in iframe