Skip to content
Browse files

You can now run Zombie without base64 module, if you're not planning

on uploading binary files.  If you are uploading binary files,
you'll need to `npm install base64` (Assaf Arkin).
  • Loading branch information...
1 parent 6b89162 commit 5eb661f6762a535bd2f916f70dd4f0c1c210fc33 @assaf assaf committed Jan 30, 2011
Showing with 82 additions and 60 deletions.
  1. +9 −0 CHANGELOG.md
  2. +1 −0 package.json
  3. +35 −35 spec/forms-spec.coffee
  4. +5 −6 spec/helpers.coffee
  5. +14 −12 src/zombie/forms.coffee
  6. +18 −7 src/zombie/resources.coffee
View
9 CHANGELOG.md
@@ -7,6 +7,15 @@ zombie.js-changelog(7) -- Changelog
Fix firing the `change` event on `SELECT` elements when using jQuery
(Damian Janowski).
+You can now run Zombie without base64 module, if you're not planning on
+uploading binary files. If you are uploading binary files, you'll need
+to `npm install base64` (Assaf Arkin).
+
+Tested with Node 0.3.7 (Assaf Arkin).
+
+ 283 Tests
+ 4.6 sec to complete
+
### Version 0.8.11 2011-01-25
View
1 package.json
@@ -38,6 +38,7 @@
"mime": "1.2.1"
},
"devDependencies": {
+ "base64": ">1.0.1",
"coffee-script": ">= 1.0.0",
"docco": "0.3.0",
"express": "1.0.3",
View
70 spec/forms-spec.coffee
@@ -2,10 +2,10 @@ require("./helpers")
{ vows: vows, assert: assert, zombie: zombie, brains: brains } = require("vows")
-brains.get "/form", (req, res)-> res.send """
+brains.get "/forms/form", (req, res)-> res.send """
<html>
<body>
- <form action="/submit" method="post">
+ <form action="/forms/submit" method="post">
<label>Name <input type="text" name="name" id="field-name"></label>
<label for="field-email">Email</label>
<input type="text" name="email" id="field-email"></label>
@@ -38,14 +38,14 @@ brains.get "/form", (req, res)-> res.send """
<option>neither</option>
</select>
- <span>First address<span>
+ <span>First address</span>
<label for='address1_street'>Street</label>
<input type="text" name="addresses[][street]" value="" id="address1_street">
<label for='address1_city'>City</label>
<input type="text" name="addresses[][city]" value="" id="address1_city">
- <span>Second address<span>
+ <span>Second address</span>
<label for='address2_street'>Street</label>
<input type="text" name="addresses[][street]" value="" id="address2_street">
@@ -79,7 +79,7 @@ brains.get "/form", (req, res)-> res.send """
</body>
</html>
"""
-brains.post "/submit", (req, res)-> res.send """
+brains.post "/forms/submit", (req, res)-> res.send """
<html>
<body>
<div id="name">#{req.body.name}</div>
@@ -101,7 +101,7 @@ brains.post "/submit", (req, res)-> res.send """
</html>
"""
-brains.get "/upload", (req, res)-> res.send """
+brains.get "/forms/upload", (req, res)-> res.send """
<html>
<body>
<form method="post" enctype="multipart/form-data">
@@ -117,7 +117,7 @@ brains.get "/upload", (req, res)-> res.send """
</body>
</html>
"""
-brains.post "/upload", (req, res)->
+brains.post "/forms/upload", (req, res)->
[text, image] = [req.body.text, req.body.image]
res.send """
<html>
@@ -128,7 +128,7 @@ brains.post "/upload", (req, res)->
vows.describe("Forms").addBatch(
"fill field":
- zombie.wants "http://localhost:3003/form"
+ zombie.wants "http://localhost:3003/forms/form"
topic: (browser)->
for field in ["email", "likes", "name", "password", "invalidtype", "email2"]
do (field)->
@@ -166,7 +166,7 @@ vows.describe("Forms").addBatch(
"should fire change event": (browser)-> assert.ok browser.email2Changed
"check box":
- zombie.wants "http://localhost:3003/form"
+ zombie.wants "http://localhost:3003/forms/form"
topic: (browser)->
for field in ["hungry", "brains", "green"]
do (field)->
@@ -196,7 +196,7 @@ vows.describe("Forms").addBatch(
"should fire change event": (browser)-> assert.ok browser.greenChanged
"radio buttons":
- zombie.wants "http://localhost:3003/form"
+ zombie.wants "http://localhost:3003/forms/form"
topic: (browser)->
for field in ["scary", "notscary"]
do (field)->
@@ -220,7 +220,7 @@ vows.describe("Forms").addBatch(
###
"select option":
- zombie.wants "http://localhost:3003/form"
+ zombie.wants "http://localhost:3003/forms/form"
topic: (browser)->
for field in ["looks", "state", "kills"]
do (field)->
@@ -256,7 +256,7 @@ vows.describe("Forms").addBatch(
"should fire change event": (browser)-> assert.ok browser.killsChanged
"multiple select option":
- zombie.wants "http://localhost:3003/form"
+ zombie.wants "http://localhost:3003/forms/form"
topic: (browser)->
browser.querySelector("#field-hobbies").addEventListener "change", -> browser["hobbiesChanged"] = true
@callback null, browser
@@ -279,7 +279,7 @@ vows.describe("Forms").addBatch(
"reset form":
"by calling reset":
- zombie.wants "http://localhost:3003/form"
+ zombie.wants "http://localhost:3003/forms/form"
topic: (browser)->
browser.fill("Name", "ArmBiter").fill("likes", "Arm Biting").
check("You bet").choose("Scary").select("state", "dead")
@@ -293,21 +293,21 @@ vows.describe("Forms").addBatch(
assert.ok browser.querySelector("#field-notscary").checked
"should reset select to original option": (browser)-> assert.equal browser.querySelector("#field-state").value, "alive"
"with event handler":
- zombie.wants "http://localhost:3003/form"
+ zombie.wants "http://localhost:3003/forms/form"
topic: (browser)->
browser.querySelector("form :reset").addEventListener "click", (event)=> @callback null, event
browser.querySelector("form :reset").click()
"should fire click event": (event)-> assert.equal event.type, "click"
"with preventDefault":
- zombie.wants "http://localhost:3003/form"
+ zombie.wants "http://localhost:3003/forms/form"
topic: (browser)->
browser.fill("Name", "ArmBiter")
browser.querySelector("form :reset").addEventListener "click", (event)-> event.preventDefault()
browser.querySelector("form :reset").click()
@callback null, browser
"should not reset input field": (browser)-> assert.equal browser.querySelector("#field-name").value, "ArmBiter"
"by clicking reset input":
- zombie.wants "http://localhost:3003/form"
+ zombie.wants "http://localhost:3003/forms/form"
topic: (browser)->
browser.fill("Name", "ArmBiter")
browser.querySelector("form :reset").click()
@@ -316,7 +316,7 @@ vows.describe("Forms").addBatch(
"submit form":
"by calling submit":
- zombie.wants "http://localhost:3003/form"
+ zombie.wants "http://localhost:3003/forms/form"
topic: (browser)->
browser.fill("Name", "ArmBiter").fill("likes", "Arm Biting").check("You bet").
check("Certainly").choose("Scary").select("state", "dead").select("looks", "Choose one").
@@ -326,7 +326,7 @@ vows.describe("Forms").addBatch(
browser.querySelector("form").submit()
browser.wait @callback
- "should open new page": (browser)-> assert.equal browser.location, "http://localhost:3003/submit"
+ "should open new page": (browser)-> assert.equal browser.location, "http://localhost:3003/forms/submit"
"should add location to history": (browser)-> assert.length browser.window.history, 2
"should send text input values to server": (browser)-> assert.equal browser.text("#name"), "ArmBiter"
"should send textarea values to server": (browser)-> assert.equal browser.text("#likes"), "Arm Biting"
@@ -349,11 +349,11 @@ vows.describe("Forms").addBatch(
assert.equal browser.text("#addresses"), '[{"street":"CDG"},{"city":"Paris"},{"street":"PGS"},{"city":"Mikolaiv"}]'
"by clicking button":
- zombie.wants "http://localhost:3003/form"
+ zombie.wants "http://localhost:3003/forms/form"
topic: (browser)->
browser.fill("Name", "ArmBiter").fill("likes", "Arm Biting").
pressButton "Hit Me", @callback
- "should open new page": (browser)-> assert.equal browser.location, "http://localhost:3003/submit"
+ "should open new page": (browser)-> assert.equal browser.location, "http://localhost:3003/forms/submit"
"should add location to history": (browser)-> assert.length browser.window.history, 2
"should send button value to server": (browser)-> assert.equal browser.text("#clicked"), "hit-me"
"should send input values to server": (browser)->
@@ -364,11 +364,11 @@ vows.describe("Forms").addBatch(
"should return status code": (_, browser, status)-> assert.equal status, 200
"by clicking image button":
- zombie.wants "http://localhost:3003/form"
+ zombie.wants "http://localhost:3003/forms/form"
topic: (browser)->
browser.fill("Name", "ArmBiter").fill("likes", "Arm Biting").
pressButton "#image_submit", @callback
- "should open new page": (browser)-> assert.equal browser.location, "http://localhost:3003/submit"
+ "should open new page": (browser)-> assert.equal browser.location, "http://localhost:3003/forms/submit"
"should add location to history": (browser)-> assert.length browser.window.history, 2
"should send image value to server": (browser)-> assert.equal browser.text("#image_clicked"), "Image Submit"
"should send input values to server": (browser)->
@@ -378,49 +378,49 @@ vows.describe("Forms").addBatch(
assert.equal browser.text("#clicked"), "undefined"
"by clicking input":
- zombie.wants "http://localhost:3003/form"
+ zombie.wants "http://localhost:3003/forms/form"
topic: (browser)->
browser.fill("Name", "ArmBiter").fill("likes", "Arm Biting").
pressButton "Submit", @callback
- "should open new page": (browser)-> assert.equal browser.location, "http://localhost:3003/submit"
+ "should open new page": (browser)-> assert.equal browser.location, "http://localhost:3003/forms/submit"
"should add location to history": (browser)-> assert.length browser.window.history, 2
"should send submit value to server": (browser)-> assert.equal browser.text("#clicked"), "Submit"
"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"
+ zombie.wants "http://localhost:3003/forms/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"
+ zombie.wants "http://localhost:3003/forms/upload"
topic: (browser)->
- @filename = __dirname + "/data/random.txt"
- browser.attach("text", @filename).pressButton "Upload", @callback
+ 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"), "random.txt"
"file upload (binary)":
- zombie.wants "http://localhost:3003/upload"
+ zombie.wants "http://localhost:3003/forms/upload"
topic: (browser)->
- @filename = __dirname + "/data/zombie.jpg"
- browser.attach("image", @filename).pressButton "Upload", @callback
+ filename = __dirname + "/data/zombie.jpg"
+ browser.attach("image", filename).pressButton "Upload", @callback
"should upload include name": (browser)-> assert.equal browser.text("title"), "zombie.jpg"
"file upload (empty)":
- zombie.wants "http://localhost:3003/upload"
+ zombie.wants "http://localhost:3003/forms/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"
+ zombie.wants "http://localhost:3003/forms/upload"
topic: (browser)->
- @filename = __dirname + "/data/random.txt"
- browser.attach("get_file", @filename).pressButton "Get Upload", @callback
+ 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"
View
11 spec/helpers.coffee
@@ -1,8 +1,8 @@
require.paths.unshift __dirname + "/../node_modules"
fs = require("fs")
-decode = require("base64").decode
express = require("express")
zombie = require("../src/index")
+debug = false # true
# When you run the vows command, it picks all the files in the spec directory
@@ -83,7 +83,7 @@ zombie.wants = (url, context)->
zombie.Browser.prototype.wants = (url, options, callback)->
brains.ready =>
- #options.debug = true
+ options.debug = debug
@visit url, options, (err, browser)=>
callback err, this if callback
return
@@ -108,10 +108,9 @@ express.bodyDecoder.decode["multipart/form-data"] = (body)->
headers
, {}
- # 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)
+ # Base64 encoding is optional.
+ if headers["content-transfer-encoding"] == "base64"
+ contents = require("base64").decode(contents)
contents.mime = headers["content-type"].split(/;/)[0]
# We're looking for the content-disposition header, which has
View
26 src/zombie/forms.coffee
@@ -3,24 +3,26 @@ core = require("jsdom").dom.level3.core
path = require("path")
fs = require("fs")
mime = require("mime")
-base64 = require("base64")
+
# The Form
# --------
-UploadedFile = (filename)->
+
+# Forms convert INPUT fields of type file into this object and pass it as
+# parameter to resource request.
+#
+# The base class is a String, so the value (e.g. when passed in a GET request)
+# is the base filename. Additional properties include the MIME type (`mime`),
+# the full filename (`filename`) and the `read` method that returns the file
+# contents.
+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
+ file.filename = filename
+ file.mime = mime.lookup(filename)
+ file.read = -> fs.readFileSync(filename)
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.
core.HTMLFormElement.prototype.submit = (button)->
View
25 src/zombie/resources.coffee
@@ -160,23 +160,34 @@ class Resources extends Array
for value in values
disp = "Content-Disposition: form-data; name=\"#{name}\""
+ encoding = null
- if value.contents
+ if value.read
+ content = value.read()
disp += "; filename=\"#{value}\""
- content = value.contents()
- mime = value.mime()
- encoding = value.encoding()
+ mime = value.mime
+ encoding = "base64" unless value.mime == "text/plain"
else
content = value
mime = "text/plain"
lines.push disp
lines.push "Content-Type: #{mime}"
lines.push "Content-Length: #{content.length}"
- lines.push "Content-Transfer-Encoding: base64" if encoding
+ lines.push "Content-Transfer-Encoding: #{encoding}" if encoding
lines.push ""
- lines.push content
-
+ switch encoding
+ when "base64"
+ # Base64 encoding is optional, loaded on demand.
+ try
+ base64 = require("base64")
+ catch ex
+ throw new Error("Base64 encoding support is optional, you need to `npm install base64`")
+ lines.push base64.encode(content).replace(/(.{76})/g, "$1\r\n")
+ when null
+ lines.push content
+ else
+ throw new Error("Unsupported transfer encoding #{encoding}")
lines.push "--#{boundary}"
)
if lines.length < 2

0 comments on commit 5eb661f

Please sign in to comment.
Something went wrong with that request. Please try again.