Skip to content
This repository was archived by the owner on Dec 16, 2023. It is now read-only.

Commit 49617bc

Browse files
committed
Merge branch 'port' of https://github.com/josevalim/zombie into josevalim-port
Conflicts: spec/helpers.coffee
2 parents 0710c87 + ae2312d commit 49617bc

File tree

4 files changed

+75
-37
lines changed

4 files changed

+75
-37
lines changed

spec/forms-spec.coffee

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ brains.get "/upload", (req, res)-> res.send """
7070
<input name="image" type="file">
7171
<button>Upload</button>
7272
</form>
73+
74+
<form>
75+
<input name="get_file" type="file">
76+
<input type="submit" value="Get Upload">
77+
</form>
7378
</body>
7479
</html>
7580
"""
@@ -78,11 +83,10 @@ brains.post "/upload", (req, res)->
7883
res.send """
7984
<html>
8085
<head><title>#{text?.filename || image?.filename}</title></head>
81-
<body>#{text || image.length}</body>
86+
<body>#{text || image?.length}</body>
8287
</html>
8388
"""
8489

85-
8690
vows.describe("Forms").addBatch(
8791
"fill field":
8892
zombie.wants "http://localhost:3003/form"
@@ -293,20 +297,40 @@ vows.describe("Forms").addBatch(
293297
"should send input values to server": (browser)->
294298
assert.equal browser.text("#name"), "ArmBiter"
295299
assert.equal browser.text("#likes"), "Arm Biting"
300+
"by cliking a button without name":
301+
zombie.wants "http://localhost:3003/upload"
302+
topic: (browser)->
303+
browser.pressButton "Get Upload", @callback
304+
"should not send inputs without names": (browser)-> assert.equal browser.location.search, "?"
296305

297306
"file upload (ascii)":
298307
zombie.wants "http://localhost:3003/upload"
299308
topic: (browser)->
300309
@filename = __dirname + "/data/random.txt"
301310
browser.attach("text", @filename).pressButton "Upload", @callback
302311
"should upload file": (browser)-> assert.equal browser.text("body").trim(), "Random text"
303-
"should upload include name": (browser)-> assert.equal browser.text("title"), @filename
312+
"should upload include name": (browser)-> assert.equal browser.text("title"), "random.txt"
304313

305314
"file upload (binary)":
306315
zombie.wants "http://localhost:3003/upload"
307316
topic: (browser)->
308317
@filename = __dirname + "/data/zombie.jpg"
309318
browser.attach("image", @filename).pressButton "Upload", @callback
310-
#"should upload file": (browser)-> assert.equal browser.text("body").trim(), "Random text"
311-
"should upload include name": (browser)-> assert.equal browser.text("title"), @filename
319+
"should upload include name": (browser)-> assert.equal browser.text("title"), "zombie.jpg"
320+
321+
"file upload (empty)":
322+
zombie.wants "http://localhost:3003/upload"
323+
topic: (browser)->
324+
browser.attach "text", ""
325+
browser.pressButton "Upload", @callback
326+
"should not upload any file": (browser)-> assert.equal browser.text("body").trim(), "undefined"
327+
328+
"file upload (get)":
329+
zombie.wants "http://localhost:3003/upload"
330+
topic: (browser)->
331+
@filename = __dirname + "/data/random.txt"
332+
browser.attach("get_file", @filename).pressButton "Get Upload", @callback
333+
"should send just the file basename": (browser)->
334+
assert.equal browser.location.search, "?get_file=random.txt"
335+
312336
).export(module)

spec/helpers.coffee

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ zombie.Browser.prototype.wants = (url, options, callback)->
9292
# Handle multipart/form-data so we can test file upload.
9393
express.bodyDecoder.decode["multipart/form-data"] = (body)->
9494
# Find the boundary
95-
if boundary = body.match(/^(--.*)\r\n(?:.|\n|\r)*\1--/m)[1]
95+
match = body.match(/^(--.*)\r\n(?:.|\n|\r)*\1--/m)
96+
if match && boundary = match[1]
9697
# Split body at boundary, ignore first (opening) and last (closing)
9798
# boundaries, and map the rest into name/value pairs.
9899
body.split("#{boundary}").slice(1,-1).reduce (parts, part)->
@@ -106,8 +107,13 @@ express.bodyDecoder.decode["multipart/form-data"] = (body)->
106107
headers[split[0].toLowerCase()] = split.slice(1).join(":").trim()
107108
headers
108109
, {}
109-
contents = decode(contents) if headers["content-transfer-encoding"] == "base64"
110+
111+
# Intentionally do not decode base64 files if the content-type is text.
112+
# This is the behavior of a few servers.
113+
if headers["content-transfer-encoding"] == "base64" && !headers["content-type"].match(/^text/)
114+
contents = decode(contents)
110115
contents.mime = headers["content-type"].split(/;/)[0]
116+
111117
# We're looking for the content-disposition header, which has
112118
# form-data follows by name/value pairs, including the field name.
113119
if disp = headers["content-disposition"]
@@ -124,6 +130,8 @@ express.bodyDecoder.decode["multipart/form-data"] = (body)->
124130
parts[pairs.name] = contents if pairs.name
125131
parts
126132
, {}
133+
else
134+
{}
127135

128136

129137
vows.zombie = zombie

src/zombie/forms.coffee

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,25 @@
11
# Patches to JSDOM for properly handling forms.
22
core = require("jsdom").dom.level3.core
3-
exec = require("child_process").exec
4-
fs = require("fs")
3+
path = require("path")
4+
fs = require("fs")
5+
mime = require("mime")
6+
base64 = require("base64")
57

68
# The Form
79
# --------
10+
UploadedFile = (filename)->
11+
file = new String(path.basename(filename))
12+
file.mime = ()-> mime.lookup(filename)
13+
file.encoding = ()->
14+
if @mime().match(/^text/)
15+
null
16+
else
17+
"base64"
18+
file.contents = ()->
19+
result = fs.readFileSync(filename)
20+
result = base64.encode(result).replace(/(.{76})/g, "$1\r\n") if @encoding() == "base64"
21+
result
22+
return file
823

924
# Implement form.submit such that it actually submits a request to the server.
1025
# This method takes the submitting button so we can send the button name/value.
@@ -14,10 +29,7 @@ core.HTMLFormElement.prototype.submit = (button)->
1429

1530
process = (index)=>
1631
if field = @elements.item(index)
17-
if field.getAttribute("disabled")
18-
process index + 1
19-
else
20-
name = field.getAttribute("name")
32+
if !field.getAttribute("disabled") && name = field.getAttribute("name")
2133
if field.nodeName == "SELECT"
2234
selected = []
2335
for option in field.options
@@ -28,28 +40,14 @@ core.HTMLFormElement.prototype.submit = (button)->
2840
else
2941
value = selected.shift()
3042
params[name] = value if value
31-
process index + 1
3243
else if field.nodeName == "INPUT" && (field.type == "checkbox" || field.type == "radio")
3344
params[name] = field.value if field.checked
34-
process index + 1
3545
else if field.nodeName == "INPUT" && field.type == "file"
36-
if filename = field.value
37-
file = fs.readFileSync(filename)
38-
file.filename = filename
39-
document.parentWindow.queue (done)->
40-
exec "file -b -I '#{filename}'", (err, stdout)->
41-
file.mime = stdout unless err
42-
file.mime = "text/plain" if file.mime == ""
43-
params[name] = file
44-
done()
45-
process index + 1
46-
else
47-
process index + 1
46+
params[name] = new UploadedFile(field.value) if field.value
4847
else if field.nodeName == "TEXTAREA" || field.nodeName == "INPUT"
4948
params[name] = field.value if field.value
50-
process index + 1
51-
else
52-
process index + 1
49+
50+
process index + 1
5351
else
5452
params[button.name] = button.value if button && button.name
5553
history = document.parentWindow.history

src/zombie/history.coffee

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
# Window history and location.
2-
encode = require("base64").encode
32
http = require("http")
43
jsdom = require("jsdom")
54
html = jsdom.dom.level3.html
@@ -95,15 +94,24 @@ class History
9594
boundary = "#{new Date().getTime()}#{Math.random()}"
9695
lines = ["--#{boundary}"]
9796
for name, value of data
98-
encoded = encode(value).replace(/(.{76})/g, "$1\r\n")
9997
disp = "Content-Disposition: form-data; name=\"#{name}\""
100-
disp += "; filename=\"#{value.filename}\"" if value.filename
98+
99+
if value.contents
100+
disp += "; filename=\"#{value}\""
101+
content = value.contents()
102+
mime = value.mime()
103+
encoding = value.encoding()
104+
else
105+
content = value
106+
mime = "text/plain"
107+
101108
lines.push disp
102-
lines.push "Content-Type: #{value.mime || "text/plain"}"
103-
lines.push "Content-Length: #{encoded.length}"
104-
lines.push "Content-Transfer-Encoding: base64"
109+
lines.push "Content-Type: #{mime}"
110+
lines.push "Content-Length: #{content.length}"
111+
lines.push "Content-Transfer-Encoding: base64" if encoding
105112
lines.push ""
106-
lines.push encoded
113+
lines.push content
114+
107115
lines.push "--#{boundary}"
108116
data = lines.join("\r\n") + "--\r\n"
109117
headers["content-type"] += "; boundary=#{boundary}"

0 commit comments

Comments
 (0)