Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'port' of https://github.com/josevalim/zombie into josev…

…alim-port

Conflicts:
	spec/helpers.coffee
  • Loading branch information...
commit 49617bccda47cea4ee8e8865c902b0ee91c4721e 2 parents 0710c87 + ae2312d
Assaf Arkin assaf authored
34 spec/forms-spec.coffee
View
@@ -70,6 +70,11 @@ brains.get "/upload", (req, res)-> res.send """
<input name="image" type="file">
<button>Upload</button>
</form>
+
+ <form>
+ <input name="get_file" type="file">
+ <input type="submit" value="Get Upload">
+ </form>
</body>
</html>
"""
@@ -78,11 +83,10 @@ brains.post "/upload", (req, res)->
res.send """
<html>
<head><title>#{text?.filename || image?.filename}</title></head>
- <body>#{text || image.length}</body>
+ <body>#{text || image?.length}</body>
</html>
"""
-
vows.describe("Forms").addBatch(
"fill field":
zombie.wants "http://localhost:3003/form"
@@ -293,6 +297,11 @@ vows.describe("Forms").addBatch(
"should send input values to server": (browser)->
assert.equal browser.text("#name"), "ArmBiter"
assert.equal browser.text("#likes"), "Arm Biting"
+ "by cliking a button without name":
+ zombie.wants "http://localhost:3003/upload"
+ topic: (browser)->
+ browser.pressButton "Get Upload", @callback
+ "should not send inputs without names": (browser)-> assert.equal browser.location.search, "?"
"file upload (ascii)":
zombie.wants "http://localhost:3003/upload"
@@ -300,13 +309,28 @@ vows.describe("Forms").addBatch(
@filename = __dirname + "/data/random.txt"
browser.attach("text", @filename).pressButton "Upload", @callback
"should upload file": (browser)-> assert.equal browser.text("body").trim(), "Random text"
- "should upload include name": (browser)-> assert.equal browser.text("title"), @filename
+ "should upload include name": (browser)-> assert.equal browser.text("title"), "random.txt"
"file upload (binary)":
zombie.wants "http://localhost:3003/upload"
topic: (browser)->
@filename = __dirname + "/data/zombie.jpg"
browser.attach("image", @filename).pressButton "Upload", @callback
- #"should upload file": (browser)-> assert.equal browser.text("body").trim(), "Random text"
- "should upload include name": (browser)-> assert.equal browser.text("title"), @filename
+ "should upload include name": (browser)-> assert.equal browser.text("title"), "zombie.jpg"
+
+ "file upload (empty)":
+ zombie.wants "http://localhost:3003/upload"
+ topic: (browser)->
+ browser.attach "text", ""
+ browser.pressButton "Upload", @callback
+ "should not upload any file": (browser)-> assert.equal browser.text("body").trim(), "undefined"
+
+ "file upload (get)":
+ zombie.wants "http://localhost:3003/upload"
+ topic: (browser)->
+ @filename = __dirname + "/data/random.txt"
+ browser.attach("get_file", @filename).pressButton "Get Upload", @callback
+ "should send just the file basename": (browser)->
+ assert.equal browser.location.search, "?get_file=random.txt"
+
).export(module)
12 spec/helpers.coffee
View
@@ -92,7 +92,8 @@ zombie.Browser.prototype.wants = (url, options, callback)->
# Handle multipart/form-data so we can test file upload.
express.bodyDecoder.decode["multipart/form-data"] = (body)->
# Find the boundary
- if boundary = body.match(/^(--.*)\r\n(?:.|\n|\r)*\1--/m)[1]
+ match = body.match(/^(--.*)\r\n(?:.|\n|\r)*\1--/m)
+ if match && boundary = match[1]
# Split body at boundary, ignore first (opening) and last (closing)
# boundaries, and map the rest into name/value pairs.
body.split("#{boundary}").slice(1,-1).reduce (parts, part)->
@@ -106,8 +107,13 @@ express.bodyDecoder.decode["multipart/form-data"] = (body)->
headers[split[0].toLowerCase()] = split.slice(1).join(":").trim()
headers
, {}
- contents = decode(contents) if headers["content-transfer-encoding"] == "base64"
+
+ # Intentionally do not decode base64 files if the content-type is text.
+ # This is the behavior of a few servers.
+ if headers["content-transfer-encoding"] == "base64" && !headers["content-type"].match(/^text/)
+ contents = decode(contents)
contents.mime = headers["content-type"].split(/;/)[0]
+
# We're looking for the content-disposition header, which has
# form-data follows by name/value pairs, including the field name.
if disp = headers["content-disposition"]
@@ -124,6 +130,8 @@ express.bodyDecoder.decode["multipart/form-data"] = (body)->
parts[pairs.name] = contents if pairs.name
parts
, {}
+ else
+ {}
vows.zombie = zombie
44 src/zombie/forms.coffee
View
@@ -1,10 +1,25 @@
# Patches to JSDOM for properly handling forms.
core = require("jsdom").dom.level3.core
-exec = require("child_process").exec
-fs = require("fs")
+path = require("path")
+fs = require("fs")
+mime = require("mime")
+base64 = require("base64")
# The Form
# --------
+UploadedFile = (filename)->
+ file = new String(path.basename(filename))
+ file.mime = ()-> mime.lookup(filename)
+ file.encoding = ()->
+ if @mime().match(/^text/)
+ null
+ else
+ "base64"
+ file.contents = ()->
+ result = fs.readFileSync(filename)
+ result = base64.encode(result).replace(/(.{76})/g, "$1\r\n") if @encoding() == "base64"
+ result
+ return file
# Implement form.submit such that it actually submits a request to the server.
# This method takes the submitting button so we can send the button name/value.
@@ -14,10 +29,7 @@ core.HTMLFormElement.prototype.submit = (button)->
process = (index)=>
if field = @elements.item(index)
- if field.getAttribute("disabled")
- process index + 1
- else
- name = field.getAttribute("name")
+ if !field.getAttribute("disabled") && name = field.getAttribute("name")
if field.nodeName == "SELECT"
selected = []
for option in field.options
@@ -28,28 +40,14 @@ core.HTMLFormElement.prototype.submit = (button)->
else
value = selected.shift()
params[name] = value if value
- process index + 1
else if field.nodeName == "INPUT" && (field.type == "checkbox" || field.type == "radio")
params[name] = field.value if field.checked
- process index + 1
else if field.nodeName == "INPUT" && field.type == "file"
- if filename = field.value
- file = fs.readFileSync(filename)
- file.filename = filename
- document.parentWindow.queue (done)->
- exec "file -b -I '#{filename}'", (err, stdout)->
- file.mime = stdout unless err
- file.mime = "text/plain" if file.mime == ""
- params[name] = file
- done()
- process index + 1
- else
- process index + 1
+ params[name] = new UploadedFile(field.value) if field.value
else if field.nodeName == "TEXTAREA" || field.nodeName == "INPUT"
params[name] = field.value if field.value
- process index + 1
- else
- process index + 1
+
+ process index + 1
else
params[button.name] = button.value if button && button.name
history = document.parentWindow.history
22 src/zombie/history.coffee
View
@@ -1,5 +1,4 @@
# Window history and location.
-encode = require("base64").encode
http = require("http")
jsdom = require("jsdom")
html = jsdom.dom.level3.html
@@ -95,15 +94,24 @@ class History
boundary = "#{new Date().getTime()}#{Math.random()}"
lines = ["--#{boundary}"]
for name, value of data
- encoded = encode(value).replace(/(.{76})/g, "$1\r\n")
disp = "Content-Disposition: form-data; name=\"#{name}\""
- disp += "; filename=\"#{value.filename}\"" if value.filename
+
+ if value.contents
+ disp += "; filename=\"#{value}\""
+ content = value.contents()
+ mime = value.mime()
+ encoding = value.encoding()
+ else
+ content = value
+ mime = "text/plain"
+
lines.push disp
- lines.push "Content-Type: #{value.mime || "text/plain"}"
- lines.push "Content-Length: #{encoded.length}"
- lines.push "Content-Transfer-Encoding: base64"
+ lines.push "Content-Type: #{mime}"
+ lines.push "Content-Length: #{content.length}"
+ lines.push "Content-Transfer-Encoding: base64" if encoding
lines.push ""
- lines.push encoded
+ lines.push content
+
lines.push "--#{boundary}"
data = lines.join("\r\n") + "--\r\n"
headers["content-type"] += "; boundary=#{boundary}"
Please sign in to comment.
Something went wrong with that request. Please try again.