Skip to content

Commit

Permalink
Merge branch 'port' of https://github.com/josevalim/zombie into josev…
Browse files Browse the repository at this point in the history
…alim-port

Conflicts:
	spec/helpers.coffee
  • Loading branch information
assaf committed Jan 4, 2011
2 parents 0710c87 + ae2312d commit 49617bc
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 37 deletions.
34 changes: 29 additions & 5 deletions spec/forms-spec.coffee
Expand Up @@ -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>
"""
Expand All @@ -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"
Expand Down Expand Up @@ -293,20 +297,40 @@ 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"
topic: (browser)->
@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 changes: 10 additions & 2 deletions spec/helpers.coffee
Expand Up @@ -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)->
Expand All @@ -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"]
Expand All @@ -124,6 +130,8 @@ express.bodyDecoder.decode["multipart/form-data"] = (body)->
parts[pairs.name] = contents if pairs.name
parts
, {}
else
{}


vows.zombie = zombie
Expand Down
44 changes: 21 additions & 23 deletions src/zombie/forms.coffee
@@ -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.
Expand All @@ -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
Expand All @@ -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
Expand Down
22 changes: 15 additions & 7 deletions src/zombie/history.coffee
@@ -1,5 +1,4 @@
# Window history and location.
encode = require("base64").encode
http = require("http")
jsdom = require("jsdom")
html = jsdom.dom.level3.html
Expand Down Expand Up @@ -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}"
Expand Down

0 comments on commit 49617bc

Please sign in to comment.