summaryrefslogtreecommitdiff
path: root/rel/overlay/share/www/spec
diff options
context:
space:
mode:
Diffstat (limited to 'rel/overlay/share/www/spec')
-rw-r--r--rel/overlay/share/www/spec/couch_js_class_methods_spec.js401
-rw-r--r--rel/overlay/share/www/spec/couch_js_instance_methods_1_spec.js311
-rw-r--r--rel/overlay/share/www/spec/couch_js_instance_methods_2_spec.js246
-rw-r--r--rel/overlay/share/www/spec/couch_js_instance_methods_3_spec.js215
-rw-r--r--rel/overlay/share/www/spec/custom_helpers.js51
-rw-r--r--rel/overlay/share/www/spec/jquery_couch_js_class_methods_spec.js523
-rw-r--r--rel/overlay/share/www/spec/jquery_couch_js_instance_methods_1_spec.js202
-rw-r--r--rel/overlay/share/www/spec/jquery_couch_js_instance_methods_2_spec.js433
-rw-r--r--rel/overlay/share/www/spec/jquery_couch_js_instance_methods_3_spec.js540
-rw-r--r--rel/overlay/share/www/spec/run.html46
10 files changed, 2968 insertions, 0 deletions
diff --git a/rel/overlay/share/www/spec/couch_js_class_methods_spec.js b/rel/overlay/share/www/spec/couch_js_class_methods_spec.js
new file mode 100644
index 00000000..7eac2348
--- /dev/null
+++ b/rel/overlay/share/www/spec/couch_js_class_methods_spec.js
@@ -0,0 +1,401 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+// Specs for couch.js lines 313-470
+
+describe 'CouchDB class'
+ describe 'session stuff'
+ before
+ useTestUserDb();
+ end
+
+ after
+ useOldUserDb();
+ end
+
+ before_each
+ userDoc = users_db.save(CouchDB.prepareUserDoc({name: "Gaius Baltar", roles: ["president"]}, "secretpass"));
+ end
+
+ after_each
+ users_db.deleteDoc({_id : userDoc.id, _rev : userDoc.rev})
+ end
+
+ describe '.login'
+ it 'should return ok true'
+ CouchDB.login("Gaius Baltar", "secretpass").ok.should.be_true
+ end
+
+ it 'should return the name of the logged in user'
+ CouchDB.login("Gaius Baltar", "secretpass").name.should.eql "Gaius Baltar"
+ end
+
+ it 'should return the roles of the logged in user'
+ CouchDB.login("Gaius Baltar", "secretpass").roles.should.eql ["president"]
+ end
+
+ it 'should post _session'
+ CouchDB.should.receive("request", "once").with_args("POST", "/_session")
+ CouchDB.login("Gaius Baltar", "secretpass");
+ end
+
+ it 'should create a session'
+ CouchDB.login("Gaius Baltar", "secretpass");
+ CouchDB.session().userCtx.name.should.eql "Gaius Baltar"
+ end
+ end
+
+ describe '.logout'
+ before_each
+ CouchDB.login("Gaius Baltar", "secretpass");
+ end
+
+ it 'should return ok true'
+ CouchDB.logout().ok.should.be_true
+ end
+
+ it 'should delete _session'
+ CouchDB.should.receive("request", "once").with_args("DELETE", "/_session")
+ CouchDB.logout();
+ end
+
+ it 'should result in an invalid session'
+ CouchDB.logout();
+ CouchDB.session().name.should.be_null
+ end
+ end
+
+ describe '.session'
+ before_each
+ CouchDB.login("Gaius Baltar", "secretpass");
+ end
+
+ it 'should return ok true'
+ CouchDB.session().ok.should.be_true
+ end
+
+ it 'should return the users name'
+ CouchDB.session().userCtx.name.should.eql "Gaius Baltar"
+ end
+
+ it 'should return the users roles'
+ CouchDB.session().userCtx.roles.should.eql ["president"]
+ end
+
+ it 'should return the name of the authentication db'
+ CouchDB.session().info.authentication_db.should.eql "spec_users_db"
+ end
+
+ it 'should return the active authentication handler'
+ CouchDB.session().info.authenticated.should.eql "cookie"
+ end
+ end
+ end
+
+ describe 'db stuff'
+ before_each
+ db = new CouchDB("spec_db", {"X-Couch-Full-Commit":"false"});
+ db.createDb();
+ end
+
+ after_each
+ db.deleteDb();
+ end
+
+ describe '.prepareUserDoc'
+ before_each
+ userDoc = CouchDB.prepareUserDoc({name: "Laura Roslin"}, "secretpass");
+ end
+
+ it 'should return the users name'
+ userDoc.name.should.eql "Laura Roslin"
+ end
+
+ it 'should prefix the id with the CouchDB user_prefix'
+ userDoc._id.should.eql "org.couchdb.user:Laura Roslin"
+ end
+
+ it 'should return the users roles'
+ var userDocWithRoles = CouchDB.prepareUserDoc({name: "William Adama", roles: ["admiral", "commander"]}, "secretpass")
+ userDocWithRoles.roles.should.eql ["admiral", "commander"]
+ end
+
+ it 'should return the hashed password'
+ userDoc.password_sha.length.should.be_at_least 30
+ userDoc.password_sha.should.be_a String
+ end
+ end
+
+ describe '.allDbs'
+ it 'should get _all_dbs'
+ CouchDB.should.receive("request", "once").with_args("GET", "/_all_dbs");
+ CouchDB.allDbs();
+ end
+
+ it 'should return an array that includes a created database'
+ temp_db = new CouchDB("temp_spec_db", {"X-Couch-Full-Commit":"false"});
+ temp_db.createDb();
+ CouchDB.allDbs().should.include("temp_spec_db");
+ temp_db.deleteDb();
+ end
+
+ it 'should return an array that does not include a database that does not exist'
+ CouchDB.allDbs().should.not.include("not_existing_temp_spec_db");
+ end
+ end
+
+ describe '.allDesignDocs'
+ it 'should return the total number of documents'
+ CouchDB.allDesignDocs().spec_db.total_rows.should.eql 0
+ db.save({'type':'battlestar', 'name':'galactica'});
+ CouchDB.allDesignDocs().spec_db.total_rows.should.eql 1
+ end
+
+ it 'should return undefined when the db does not exist'
+ CouchDB.allDesignDocs().non_existing_db.should.be_undefined
+ end
+
+ it 'should return no documents when there are no design documents'
+ CouchDB.allDesignDocs().spec_db.rows.should.eql []
+ end
+
+ it 'should return all design documents'
+ var designDoc = {
+ "views" : {
+ "people" : {
+ "map" : "function(doc) { emit(doc._id, doc); }"
+ }
+ },
+ "_id" : "_design/spec_db"
+ };
+ db.save(designDoc);
+
+ var allDesignDocs = CouchDB.allDesignDocs();
+ allDesignDocs.spec_db.rows[0].id.should.eql "_design/spec_db"
+ allDesignDocs.spec_db.rows[0].key.should.eql "_design/spec_db"
+ allDesignDocs.spec_db.rows[0].value.rev.length.should.be_at_least 30
+ end
+ end
+
+ describe '.getVersion'
+ it 'should get the CouchDB version'
+ CouchDB.should.receive("request", "once").with_args("GET", "/")
+ CouchDB.getVersion();
+ end
+
+ it 'should return the CouchDB version'
+ CouchDB.getVersion().should_match /^\d\d?\.\d\d?\.\d\d?.*/
+ end
+ end
+
+ describe '.replicate'
+ before_each
+ db2 = new CouchDB("spec_db_2", {"X-Couch-Full-Commit":"false"});
+ db2.createDb();
+ host = window.location.protocol + "//" + window.location.host ;
+ end
+
+ after_each
+ db2.deleteDb();
+ end
+
+ it 'should return no_changes true when there are no changes between the dbs'
+ CouchDB.replicate(host + db.uri, host + db2.uri).no_changes.should.be_true
+ end
+
+ it 'should return the session ID'
+ db.save({'type':'battlestar', 'name':'galactica'});
+ CouchDB.replicate(host + db.uri, host + db2.uri).session_id.length.should.be_at_least 30
+ end
+
+ it 'should return source_last_seq'
+ db.save({'type':'battlestar', 'name':'galactica'});
+ db.save({'type':'battlestar', 'name':'pegasus'});
+
+ CouchDB.replicate(host + db.uri, host + db2.uri).source_last_seq.should.eql 2
+ end
+
+ it 'should return the replication history'
+ db.save({'type':'battlestar', 'name':'galactica'});
+ db.save({'type':'battlestar', 'name':'pegasus'});
+
+ var result = CouchDB.replicate(host + db.uri, host + db2.uri);
+ result.history[0].docs_written.should.eql 2
+ result.history[0].start_last_seq.should.eql 0
+ end
+
+ it 'should pass through replication options'
+ db.save({'type':'battlestar', 'name':'galactica'});
+ db2.deleteDb();
+ -{CouchDB.replicate(host + db.uri, host + db2.uri)}.should.throw_error
+ var result = CouchDB.replicate(host + db.uri, host + db2.uri, {"body" : {"create_target":true}});
+
+ result.ok.should.eql true
+ result.history[0].docs_written.should.eql 1
+ db2.info().db_name.should.eql "spec_db_2"
+ end
+ end
+
+ describe '.newXhr'
+ it 'should return a XMLHTTPRequest'
+ CouchDB.newXhr().should.have_prop 'readyState'
+ CouchDB.newXhr().should.have_prop 'responseText'
+ CouchDB.newXhr().should.have_prop 'status'
+ end
+ end
+
+ describe '.request'
+ it 'should return a XMLHttpRequest'
+ var req = CouchDB.request("GET", '/');
+ req.should.include "readyState"
+ req.should.include "responseText"
+ req.should.include "statusText"
+ end
+
+ it 'should pass through the options headers'
+ var xhr = CouchDB.newXhr();
+ stub(CouchDB, 'newXhr').and_return(xhr);
+
+ xhr.should.receive("setRequestHeader", "once").with_args("X-Couch-Full-Commit", "true")
+ CouchDB.request("GET", "/", {'headers': {"X-Couch-Full-Commit":"true"}});
+ end
+
+ it 'should pass through the options body'
+ var xhr = CouchDB.newXhr();
+ stub(CouchDB, 'newXhr').and_return(xhr);
+
+ xhr.should.receive("send", "once").with_args({"body_key":"body_value"})
+ CouchDB.request("GET", "/", {'body': {"body_key":"body_value"}});
+ end
+
+ it 'should prepend the urlPrefix to the uri'
+ var oldPrefix = CouchDB.urlPrefix;
+ CouchDB.urlPrefix = "/_utils";
+
+ var xhr = CouchDB.newXhr();
+ stub(CouchDB, 'newXhr').and_return(xhr);
+
+ xhr.should.receive("open", "once").with_args("GET", "/_utils/", false)
+ CouchDB.request("GET", "/", {'headers': {"X-Couch-Full-Commit":"true"}});
+
+ CouchDB.urlPrefix = oldPrefix;
+ end
+ end
+
+ describe '.requestStats'
+ it 'should get the stats for specified module and key'
+ var stats = CouchDB.requestStats('couchdb', 'open_databases', null);
+ stats.description.should.eql 'number of open databases'
+ stats.current.should.be_a Number
+ end
+
+ it 'should add flush true to the request when there is a test argument'
+ CouchDB.should.receive("request", "once").with_args("GET", "/_stats/httpd/requests?flush=true")
+ CouchDB.requestStats('httpd', 'requests', 'test');
+ end
+
+ it 'should still work when there is a test argument'
+ var stats = CouchDB.requestStats('httpd_status_codes', '200', 'test');
+ stats.description.should.eql 'number of HTTP 200 OK responses'
+ stats.sum.should.be_a Number
+ end
+ end
+
+ describe '.newUuids'
+ after_each
+ CouchDB.uuids_cache = [];
+ end
+
+ it 'should return the specified amount of uuids'
+ var uuids = CouchDB.newUuids(45);
+ uuids.should.have_length 45
+ end
+
+ it 'should return an array with uuids'
+ var uuids = CouchDB.newUuids(1);
+ uuids[0].should.be_a String
+ uuids[0].should.have_length 32
+ end
+
+ it 'should leave the uuids_cache with 100 uuids when theres no buffer size specified'
+ CouchDB.newUuids(23);
+ CouchDB.uuids_cache.should.have_length 100
+ end
+
+ it 'should leave the uuids_cache with the specified buffer size'
+ CouchDB.newUuids(23, 150);
+ CouchDB.uuids_cache.should.have_length 150
+ end
+
+ it 'should get the uuids from the uuids_cache when there are enough uuids in there'
+ CouchDB.newUuids(10);
+ CouchDB.newUuids(25);
+ CouchDB.uuids_cache.should.have_length 75
+ end
+
+ it 'should create new uuids and add as many as specified to the uuids_cache when there are not enough uuids in the cache'
+ CouchDB.newUuids(10);
+ CouchDB.newUuids(125, 60);
+ CouchDB.uuids_cache.should.have_length 160
+ end
+ end
+
+ describe '.maybeThrowError'
+ it 'should throw an error when the request has status 404'
+ var req = CouchDB.request("GET", "/nonexisting_db");
+ -{CouchDB.maybeThrowError(req)}.should.throw_error
+ end
+
+ it 'should throw an error when the request has status 412'
+ var req = CouchDB.request("PUT", "/spec_db");
+ -{CouchDB.maybeThrowError(req)}.should.throw_error
+ end
+
+ it 'should throw an error when the request has status 405'
+ var req = CouchDB.request("DELETE", "/_utils");
+ -{CouchDB.maybeThrowError(req)}.should.throw_error
+ end
+
+ it 'should throw the responseText of the request'
+ var req = CouchDB.request("GET", "/nonexisting_db");
+ try {
+ CouchDB.maybeThrowError(req)
+ } catch(e) {
+ e.error.should.eql JSON.parse(req.responseText).error
+ e.reason.should.eql JSON.parse(req.responseText).reason
+ }
+ end
+
+ it 'should throw an unknown error when the responseText is invalid json'
+ mock_request().and_return("invalid json...", "application/json", 404, {})
+ try {
+ CouchDB.maybeThrowError(CouchDB.newXhr())
+ } catch(e) {
+ e.error.should.eql "unknown"
+ e.reason.should.eql "invalid json..."
+ }
+ end
+ end
+
+ describe '.params'
+ it 'should turn a json object into a http params string'
+ var params = CouchDB.params({"president":"laura", "cag":"lee"})
+ params.should.eql "president=laura&cag=lee"
+ end
+
+ it 'should return a blank string when the object is empty'
+ var params = CouchDB.params({})
+ params.should.eql ""
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/rel/overlay/share/www/spec/couch_js_instance_methods_1_spec.js b/rel/overlay/share/www/spec/couch_js_instance_methods_1_spec.js
new file mode 100644
index 00000000..7f23bd2c
--- /dev/null
+++ b/rel/overlay/share/www/spec/couch_js_instance_methods_1_spec.js
@@ -0,0 +1,311 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+// Specs for couch.js lines 1-130
+
+describe 'CouchDB instance'
+ before_each
+ db = new CouchDB("spec_db", {"X-Couch-Full-Commit":"false"});
+ end
+
+ describe '.request'
+ before_each
+ db.createDb();
+ end
+
+ after_each
+ db.deleteDb();
+ end
+
+ it 'should return a XMLHttpRequest'
+ var req = db.request("GET", "/spec_db");
+ req.should.include "readyState"
+ req.should.include "responseText"
+ req.should.include "statusText"
+ // in Safari a XMLHttpRequest is actually a XMLHttpRequestConstructor,
+ // otherwise we could just do:
+ // req.should.be_a XMLHttpRequest
+ end
+
+ it 'should set the options the CouchDB instance has got as httpHeaders'
+ CouchDB.should.receive("request", "once").with_args("GET", "/spec_db", {headers: {"X-Couch-Full-Commit": "false"}})
+ db.request("GET", "/spec_db");
+ end
+
+ it 'should pass through the options'
+ CouchDB.should.receive("request", "once").with_args("GET", "/spec_db", {"X-Couch-Persist": "true", headers: {"X-Couch-Full-Commit": "false"}})
+ db.request("GET", "/spec_db", {"X-Couch-Persist":"true"});
+ end
+ end
+
+ describe '.createDb'
+ after_each
+ db.deleteDb();
+ end
+
+ it 'should create the db'
+ db.createDb();
+ db.last_req.status.should.eql 201
+ end
+
+ it 'should return the ok true'
+ db.createDb().should.eql {"ok" : true}
+ end
+
+ it 'should result in a created db'
+ db.createDb();
+ try{
+ db.createDb();
+ } catch(e) {
+ e.error.should.eql "file_exists"
+ }
+ end
+
+ it 'should have create a db with update sequence 0'
+ db.createDb();
+ db.info().update_seq.should.eql 0
+ end
+ end
+
+ describe '.deleteDb'
+ before_each
+ db.createDb();
+ end
+
+ it 'should delete the db'
+ db.deleteDb();
+ db.last_req.status.should.eql 200
+ end
+
+ it 'should return the responseText of the request'
+ db.deleteDb().should.eql {"ok" : true}
+ end
+
+ it 'should result in a deleted db'
+ db.deleteDb();
+ db.deleteDb();
+ db.last_req.status.should.eql 404
+ end
+ end
+
+ describe 'document methods'
+ before_each
+ doc = {"Name" : "Kara Thrace", "Callsign" : "Starbuck"};
+ db.createDb();
+ end
+
+ after_each
+ db.deleteDb();
+ end
+
+ describe '.save'
+ it 'should save the document'
+ db.save(doc);
+ db.last_req.status.should.eql 201
+ end
+
+ it 'should return ok true'
+ db.save(doc).ok.should.be_true
+ end
+
+ it 'should return ID and revision of the document'
+ var response = db.save(doc);
+ response.id.should.be_a String
+ response.id.should.have_length 32
+ response.rev.should.be_a String
+ response.rev.length.should.be_at_least 30
+ end
+
+ it 'should result in a saved document with generated ID'
+ var response = db.save(doc);
+ var saved_doc = db.open(response.id);
+ saved_doc.Name.should.eql "Kara Thrace"
+ saved_doc.Callsign.should.eql "Starbuck"
+ end
+
+ it 'should save the document with the specified ID'
+ doc._id = "123";
+ var response = db.save(doc);
+ response.id.should.eql "123"
+ end
+
+ it 'should pass through the options'
+ doc._id = "123";
+ CouchDB.should.receive("request", "once").with_args("PUT", "/spec_db/123?batch=ok")
+ db.save(doc, {"batch" : "ok"});
+ end
+ end
+
+ describe '.open'
+ before_each
+ doc._id = "123";
+ db.save(doc);
+ end
+
+ it 'should open the document'
+ db.open("123").should.eql doc
+ end
+
+ it 'should return null when there is no document with the given ID'
+ db.open("non_existing").should.be_null
+ end
+
+ it 'should pass through the options'
+ CouchDB.should.receive("request", "once").with_args("GET", "/spec_db/123?revs=true")
+ db.open("123", {"revs" : "true"});
+ end
+ end
+
+ describe '.deleteDoc'
+ before_each
+ doc._id = "123";
+ saved_doc = db.save(doc);
+ delete_response = db.deleteDoc({_id : "123", _rev : saved_doc.rev});
+ delete_last_req = db.last_req;
+ db.open("123");
+ end
+
+ it 'should send a successful request'
+ delete_last_req.status.should.eql 200
+ end
+
+ it 'should result in a deleted document'
+ db.open("123").should.be_null
+ end
+
+ it 'should return ok true, the ID and the revision of the deleted document'
+ delete_response.ok.should.be_true
+ delete_response.id.should.eql "123"
+ delete_response.rev.should.be_a String
+ delete_response.rev.length.should.be_at_least 30
+ end
+
+ it 'should mark the document as deleted'
+ var responseText = db.request("GET", "/spec_db/123").responseText;
+ JSON.parse(responseText).should.eql {"error":"not_found","reason":"deleted"}
+ end
+
+ it 'should record the revision in the deleted document'
+ var responseText = db.request("GET", "/spec_db/123?rev=" + delete_response.rev).responseText;
+ var deleted_doc = JSON.parse(responseText);
+ deleted_doc._rev.should.eql delete_response.rev
+ deleted_doc._id.should.eql delete_response.id
+ deleted_doc._deleted.should.be_true
+ end
+ end
+
+ describe '.deleteDocAttachment'
+ before_each
+ doc._id = "123";
+ doc._attachments = {
+ "friend.txt" : {
+ "content_type": "text\/plain",
+ // base64 encoded
+ "data": "TGVlIEFkYW1hIGlzIGEgZm9ybWVyIENvbG9uaWFsIEZsZWV0IFJlc2VydmUgb2ZmaWNlci4="
+ }
+ };
+ saved_doc = db.save(doc);
+ end
+
+ it 'should be executed on a document with attachment'
+ db.open("123")._attachments.should.include "friend.txt"
+ db.open("123")._attachments["friend.txt"].stub.should.be_true
+ end
+
+ describe 'after delete'
+ before_each
+ delete_response = db.deleteDocAttachment({_id : "123", _rev : saved_doc.rev}, "friend.txt");
+ db.open("123");
+ end
+
+ it 'should send a successful request'
+ db.last_req.status.should.eql 200
+ end
+
+ it 'should leave the document untouched'
+ db.open("123").Callsign.should.eql "Starbuck"
+ end
+
+ it 'should result in a deleted document attachment'
+ db.open("123").should.not.include "_attachments"
+ end
+
+ it 'should record the revision in the document whose attachment has been deleted'
+ var responseText = db.request("GET", "/spec_db/123?rev=" + delete_response.rev).responseText;
+ var deleted_doc = JSON.parse(responseText);
+ deleted_doc._rev.should.eql delete_response.rev
+ deleted_doc._id.should.eql delete_response.id
+ end
+
+ it 'should return ok true, the ID and the revision of the document whose attachment has been deleted'
+ delete_response.ok.should.be_true
+ delete_response.id.should.eql "123"
+ delete_response.should.have_property 'rev'
+ end
+ end
+ end
+
+ describe '.bulkSave'
+ before_each
+ doc = {"Name" : "Kara Thrace", "Callsign" : "Starbuck"};
+ doc2 = {"Name" : "Karl C. Agathon", "Callsign" : "Helo"};
+ doc3 = {"Name" : "Sharon Valerii", "Callsign" : "Boomer"};
+ docs = [doc, doc2, doc3];
+ end
+
+ it 'should save the documents'
+ db.bulkSave(docs);
+ db.last_req.status.should.eql 201
+ end
+
+ it 'should return ID and revision of the documents'
+ var response = db.bulkSave(docs);
+ response[0].id.should.be_a String
+ response[0].id.should.have_length 32
+ response[0].rev.should.be_a String
+ response[0].rev.length.should.be_at_least 30
+ response[1].id.should.be_a String
+ response[1].id.should.have_length 32
+ response[1].rev.should.be_a String
+ response[1].rev.length.should.be_at_least 30
+ response[2].id.should.be_a String
+ response[2].id.should.have_length 32
+ response[2].rev.should.be_a String
+ response[2].rev.length.should.be_at_least 30
+ end
+
+ it 'should result in saved documents'
+ var response = db.bulkSave(docs);
+ db.open(response[0].id).Name.should.eql "Kara Thrace"
+ db.open(response[1].id).Name.should.eql "Karl C. Agathon"
+ db.open(response[2].id).Name.should.eql "Sharon Valerii"
+ end
+
+ it 'should save the document with specified IDs'
+ doc._id = "123";
+ doc2._id = "456";
+ docs = [doc, doc2, doc3];
+ var response = db.bulkSave(docs);
+ response[0].id.should.eql "123"
+ response[1].id.should.eql "456"
+ response[2].id.should.have_length 32
+ end
+
+ it 'should pass through the options'
+ doc._id = "123";
+ docs = [doc];
+ CouchDB.should.receive("request", "once").with_args("POST", "/spec_db/_bulk_docs", {body: '{"docs":[{"Name":"Kara Thrace","Callsign":"Starbuck","_id":"123"}],"batch":"ok"}'})
+ db.bulkSave(docs, {"batch" : "ok"});
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/rel/overlay/share/www/spec/couch_js_instance_methods_2_spec.js b/rel/overlay/share/www/spec/couch_js_instance_methods_2_spec.js
new file mode 100644
index 00000000..76df6368
--- /dev/null
+++ b/rel/overlay/share/www/spec/couch_js_instance_methods_2_spec.js
@@ -0,0 +1,246 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+// Specs for couch.js lines 132-199
+
+describe 'CouchDB instance'
+ before_each
+ db = new CouchDB("spec_db", {"X-Couch-Full-Commit":"false"});
+ db.createDb();
+ end
+
+ after_each
+ db.deleteDb();
+ end
+
+ describe '.ensureFullCommit'
+ it 'should return ok true'
+ db.ensureFullCommit().ok.should.be_true
+ end
+
+ it 'should return the instance start time'
+ db.ensureFullCommit().instance_start_time.should.have_length 16
+ end
+
+ it 'should post _ensure_full_commit to the db'
+ db.should.receive("request", "once").with_args("POST", "/spec_db/_ensure_full_commit")
+ db.ensureFullCommit();
+ end
+ end
+
+ describe '.query'
+ before_each
+ db.save({"Name" : "Cally Tyrol", "job" : "deckhand", "_id" : "789"});
+ db.save({"Name" : "Felix Gaeta", "job" : "officer", "_id" : "123"});
+ db.save({"Name" : "Samuel T. Anders", "job" : "pilot", "_id" : "456"});
+ map_function = "function(doc) { emit(doc._id, 1); }";
+ reduce_function = "function(key, values, rereduce) { return sum(values); }";
+ end
+
+ it 'should apply the map function'
+ var result = db.query(map_function);
+
+ result.rows.should.have_length 3
+ result.rows[0].id.should.eql "123"
+ result.rows[0].key.should.eql "123"
+ result.rows[0].value.should.eql 1
+ result.rows[1].id.should.eql "456"
+ result.rows[1].key.should.eql "456"
+ result.rows[1].value.should.eql 1
+ result.rows[2].id.should.eql "789"
+ result.rows[2].key.should.eql "789"
+ result.rows[2].value.should.eql 1
+ end
+
+ it 'should apply the reduce function'
+ var result = db.query(map_function, reduce_function);
+
+ result.rows.should.have_length 1
+ result.rows[0].key.should.be_null
+ result.rows[0].value.should_eql 3
+ end
+
+ it 'should pass through the options'
+ var result = db.query(map_function, null, {"startkey":"456"});
+
+ result.rows.should.have_length 2
+ result.rows[0].id.should.eql "456"
+ result.rows[0].key.should.eql "456"
+ result.rows[0].value.should.eql 1
+ result.rows[1].id.should.eql "789"
+ result.rows[1].key.should.eql "789"
+ result.rows[1].value.should.eql 1
+ end
+
+ it 'should pass through the keys'
+ var result = db.query(map_function, null, {}, ["456", "123"]);
+
+ result.rows.should.have_length 2
+ result.rows[0].id.should.eql "456"
+ result.rows[0].key.should.eql "456"
+ result.rows[0].value.should.eql 1
+ result.rows[1].id.should.eql "123"
+ result.rows[1].key.should.eql "123"
+ result.rows[1].value.should.eql 1
+ end
+
+ it 'should pass through the options and the keys'
+ var result = db.query(map_function, null, {"include_docs":"true"}, ["456"]);
+
+ result.rows.should.have_length 1
+ result.rows[0].id.should.eql "456"
+ result.rows[0].key.should.eql "456"
+ result.rows[0].value.should.eql 1
+ result.rows[0].doc["job"].should.eql "pilot"
+ result.rows[0].doc["_rev"].length.should.be_at_least 30
+ end
+
+ it 'should apply a view in erlang also'
+ // when this test fails, read this: http://wiki.apache.org/couchdb/EnableErlangViews
+ var erlang_map = 'fun({Doc}) -> ' +
+ 'ID = proplists:get_value(<<"_id">>, Doc, null), ' +
+ 'Emit(ID, 1) ' +
+ 'end.';
+ var result = db.query(erlang_map, null, null, null, "erlang");
+
+ result.rows.should.have_length 3
+ result.rows[0].id.should.eql "123"
+ result.rows[0].key.should.eql "123"
+ result.rows[0].value.should.eql 1
+ result.rows[1].id.should.eql "456"
+ result.rows[1].key.should.eql "456"
+ result.rows[1].value.should.eql 1
+ result.rows[2].id.should.eql "789"
+ result.rows[2].key.should.eql "789"
+ result.rows[2].value.should.eql 1
+ end
+ end
+
+ describe '.view'
+ before_each
+ db.save({"Name" : "Cally Tyrol", "job" : "deckhand", "_id" : "789"});
+ db.save({"Name" : "Felix Gaeta", "job" : "officer", "_id" : "123"});
+ db.save({"Name" : "Samuel T. Anders", "job" : "pilot", "_id" : "456"});
+ view = {
+ "views" : {
+ "people" : {
+ "map" : "function(doc) { emit(doc._id, doc.Name); }"
+ }
+ },
+ "_id" : "_design/spec_db"
+ };
+ db.save(view);
+ end
+
+ it 'should apply the view'
+ var result = db.view('spec_db/people');
+
+ result.rows.should.have_length 3
+ result.rows[0].id.should.eql "123"
+ result.rows[0].key.should.eql "123"
+ result.rows[0].value.should.eql "Felix Gaeta"
+ result.rows[1].id.should.eql "456"
+ result.rows[1].key.should.eql "456"
+ result.rows[1].value.should.eql "Samuel T. Anders"
+ result.rows[2].id.should.eql "789"
+ result.rows[2].key.should.eql "789"
+ result.rows[2].value.should.eql "Cally Tyrol"
+ end
+
+ it 'should pass through the options'
+ var result = db.view('spec_db/people', {"skip":"2"});
+
+ result.rows.should.have_length 1
+ result.rows[0].id.should.eql "789"
+ result.rows[0].key.should.eql "789"
+ result.rows[0].value.should.eql "Cally Tyrol"
+ end
+
+ it 'should pass through the keys'
+ var result = db.view('spec_db/people', {}, ["456", "123"]);
+
+ result.rows.should.have_length 2
+ result.rows[0].id.should.eql "456"
+ result.rows[0].key.should.eql "456"
+ result.rows[0].value.should.eql "Samuel T. Anders"
+ result.rows[1].id.should.eql "123"
+ result.rows[1].key.should.eql "123"
+ result.rows[1].value.should.eql "Felix Gaeta"
+ end
+
+ it 'should pass through the options and the keys'
+ var result = db.view('spec_db/people', {"include_docs":"true"}, ["456"]);
+
+ result.rows.should.have_length 1
+ result.rows[0].id.should.eql "456"
+ result.rows[0].key.should.eql "456"
+ result.rows[0].value.should.eql "Samuel T. Anders"
+ result.rows[0].doc["job"].should.eql "pilot"
+ result.rows[0].doc["_rev"].length.should.be_at_least 30
+ end
+
+ it 'should return null when the view doesnt exist'
+ var result = db.view('spec_db/non_existing_view');
+
+ result.should.be_null
+ end
+ end
+
+ describe '.info'
+ before_each
+ result = db.info();
+ end
+
+ it 'should return the name of the database'
+ result.db_name.should.eql "spec_db"
+ end
+
+ it 'should return the number of documents'
+ result.doc_count.should.eql 0
+ end
+
+ it 'should return the start time of the db instance'
+ result.instance_start_time.should.have_length 16
+ end
+ end
+
+ describe '.designInfo'
+ before_each
+ designDoc = {
+ "views" : {
+ "people" : {
+ "map" : "function(doc) { emit(doc._id, doc); }"
+ }
+ },
+ "_id" : "_design/spec_db"
+ };
+ db.save(designDoc);
+ result = db.designInfo("_design/spec_db");
+ end
+
+ it 'should return the database name'
+ result.name.should.eql "spec_db"
+ end
+
+ it 'should return a views language'
+ result.view_index.language.should.eql "javascript"
+ end
+
+ it 'should return a views update sequence'
+ result.view_index.update_seq.should.eql 0
+ end
+
+ it 'should return a views signature'
+ result.view_index.signature.should.have_length 32
+ end
+ end
+end \ No newline at end of file
diff --git a/rel/overlay/share/www/spec/couch_js_instance_methods_3_spec.js b/rel/overlay/share/www/spec/couch_js_instance_methods_3_spec.js
new file mode 100644
index 00000000..b7464c01
--- /dev/null
+++ b/rel/overlay/share/www/spec/couch_js_instance_methods_3_spec.js
@@ -0,0 +1,215 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+// Specs for couch.js lines 201-265
+
+describe 'CouchDB instance'
+ before_each
+ db = new CouchDB("spec_db", {"X-Couch-Full-Commit":"false"});
+ db.createDb();
+ end
+
+ after_each
+ db.deleteDb();
+ end
+
+ describe '.allDocs'
+ it 'should return no docs when there arent any'
+ db.allDocs().total_rows.should.eql 0
+ db.allDocs().rows.should.eql []
+ end
+
+ describe 'with docs'
+ before_each
+ db.save({"Name" : "Felix Gaeta", "_id" : "123"});
+ db.save({"Name" : "Samuel T. Anders", "_id" : "456"});
+ end
+
+ it 'should return all docs'
+ var result = db.allDocs();
+
+ result.total_rows.should.eql 2
+ result.rows.should.have_length 2
+ result.rows[0].id.should.eql "123"
+ result.rows[0].key.should.eql "123"
+ result.rows[0].value.rev.length.should.be_at_least 30
+ result.rows[1].id.should.eql "456"
+ end
+
+ it 'should pass through the options'
+ var result = db.allDocs({"startkey": "123", "limit": "1"});
+
+ result.rows.should.have_length 1
+ result.rows[0].id.should.eql "123"
+ end
+
+ it 'should pass through the keys'
+ var result = db.allDocs({}, ["456"]);
+
+ result.rows.should.have_length 1
+ result.rows[0].id.should.eql "456"
+ result.rows[0].key.should.eql "456"
+ result.rows[0].value.rev.length.should.be_at_least 30
+ end
+
+ it 'should pass through the options and the keys'
+ var result = db.allDocs({"include_docs":"true"}, ["456"]);
+
+ result.rows.should.have_length 1
+ result.rows[0].id.should.eql "456"
+ result.rows[0].key.should.eql "456"
+ result.rows[0].value.rev.length.should.be_at_least 30
+ result.rows[0].doc["Name"].should.eql "Samuel T. Anders"
+ result.rows[0].doc["_rev"].length.should.be_at_least 30
+ end
+
+ end
+ end
+
+ describe '.designDocs'
+ it 'should return nothing when there arent any design docs'
+ db.save({"Name" : "Felix Gaeta", "_id" : "123"});
+ db.designDocs().rows.should.eql []
+ end
+
+ it 'should return all design docs'
+ var designDoc = {
+ "views" : {
+ "people" : {
+ "map" : "function(doc) { emit(doc._id, doc); }"
+ }
+ },
+ "_id" : "_design/spec_db"
+ };
+ db.save(designDoc);
+ db.save({"Name" : "Felix Gaeta", "_id" : "123"});
+
+ var result = db.designDocs();
+
+ result.total_rows.should.eql 2
+ result.rows.should.have_length 1
+ result.rows[0].id.should.eql "_design/spec_db"
+ result.rows[0].key.should.eql "_design/spec_db"
+ result.rows[0].value.rev.length.should.be_at_least 30
+ end
+ end
+
+ describe '.changes'
+ it 'should return no changes when there arent any'
+ db.changes().last_seq.should.eql 0
+ db.changes().results.should.eql []
+ end
+
+ describe 'with changes'
+ before_each
+ db.save({"Name" : "Felix Gaeta", "_id" : "123"});
+ db.save({"Name" : "Samuel T. Anders", "_id" : "456"});
+ end
+
+ it 'should return changes'
+ var result = db.changes();
+
+ result.last_seq.should.eql 2
+ result.results[0].id.should.eql "123"
+ result.results[0].seq.should.eql 1
+ result.results[0].changes[0].rev.length.should.be_at_least 30
+ result.results[1].id.should.eql "456"
+ result.results[1].seq.should.eql 2
+ result.results[1].changes[0].rev.length.should.be_at_least 30
+ end
+
+ it 'should pass through the options'
+ var result = db.changes({"since":"1"});
+
+ result.last_seq.should.eql 2
+ result.results[0].id.should.eql "456"
+ end
+ end
+ end
+
+ describe '.compact'
+ it 'should return ok true'
+ db.compact().ok.should.be_true
+ end
+
+ it 'should post _compact to the db'
+ db.should.receive("request", "once").with_args("POST", "/spec_db/_compact")
+ db.compact();
+ end
+ end
+
+ describe '.viewCleanup'
+ it 'should return ok true'
+ db.viewCleanup().ok.should.be_true
+ end
+
+ it 'should post _view_cleanup to the db'
+ db.should.receive("request", "once").with_args("POST", "/spec_db/_view_cleanup")
+ db.viewCleanup();
+ end
+ end
+
+ describe '.setDbProperty'
+ it 'should return ok true'
+ db.setDbProperty("_revs_limit", 1500).ok.should.be_true
+ end
+
+ it 'should set a db property'
+ db.setDbProperty("_revs_limit", 1500);
+ db.getDbProperty("_revs_limit").should.eql 1500
+ db.setDbProperty("_revs_limit", 1200);
+ db.getDbProperty("_revs_limit").should.eql 1200
+ end
+ end
+
+ describe '.getDbProperty'
+ it 'should get a db property'
+ db.setDbProperty("_revs_limit", 1200);
+ db.getDbProperty("_revs_limit").should.eql 1200
+ end
+
+ it 'should throw an error when the property doesnt exist'
+ function(){ db.getDbProperty("_doesnt_exist")}.should.throw_error
+ end
+ end
+
+ describe '.setSecObj'
+ it 'should return ok true'
+ db.setSecObj({"readers":{"names":["laura"],"roles":["president"]}}).ok.should.be_true
+ end
+
+ it 'should save a well formed object into the _security object '
+ db.should.receive("request", "once").with_args("PUT", "/spec_db/_security", {body: '{"readers":{"names":["laura"],"roles":["president"]}}'})
+ db.setSecObj({"readers": {"names" : ["laura"], "roles" : ["president"]}})
+ end
+
+ it 'should throw an error when the readers or admins object is malformed'
+ -{ db.setSecObj({"admins":["cylon"]}) }.should.throw_error
+ end
+
+ it 'should save any other object into the _security object'
+ db.setSecObj({"something" : "anything"})
+ db.getSecObj().should.eql {"something" : "anything"}
+ end
+ end
+
+ describe '.getSecObj'
+ it 'should get the security object'
+ db.setSecObj({"admins" : {"names" : ["bill"], "roles" : ["admiral"]}})
+ db.getSecObj().should.eql {"admins" : {"names": ["bill"], "roles": ["admiral"]}}
+ end
+
+ it 'should return an empty object when there is no security object'
+ db.getSecObj().should.eql {}
+ end
+ end
+end \ No newline at end of file
diff --git a/rel/overlay/share/www/spec/custom_helpers.js b/rel/overlay/share/www/spec/custom_helpers.js
new file mode 100644
index 00000000..d29ee87b
--- /dev/null
+++ b/rel/overlay/share/www/spec/custom_helpers.js
@@ -0,0 +1,51 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+function stubAlert(){
+ if(typeof(old_alert) == 'undefined'){
+ old_alert = alert;
+ }
+ alert = function(msg){
+ alert_msg = msg;
+ };
+}
+
+function destubAlert(){
+ alert = old_alert;
+}
+
+function errorCallback(status, error, reason){
+ console.log("Unexpected " + status + " error: " + error + " - " + reason)
+ throw("Unexpected " + status + " error: " + error + " - " + reason);
+}
+
+function successCallback(resp){
+ console.log("No error message here unexpectedly, successful response instead.")
+ throw("No error message here unexpectedly, successful response instead.");
+}
+
+function useTestUserDb(){
+ users_db = new CouchDB("spec_users_db");
+ var xhr = CouchDB.request("PUT", "/_config/couch_httpd_auth/authentication_db", {
+ body: JSON.stringify("spec_users_db")
+ });
+ if(typeof(old_auth_db) == 'undefined'){
+ old_auth_db = xhr.responseText.replace(/\n/,'').replace(/"/g,'');
+ }
+}
+
+function useOldUserDb(){
+ CouchDB.request("PUT", "/_config/couch_httpd_auth/authentication_db", {
+ body: JSON.stringify(old_auth_db)
+ });
+ users_db.deleteDb();
+} \ No newline at end of file
diff --git a/rel/overlay/share/www/spec/jquery_couch_js_class_methods_spec.js b/rel/overlay/share/www/spec/jquery_couch_js_class_methods_spec.js
new file mode 100644
index 00000000..f2df81b3
--- /dev/null
+++ b/rel/overlay/share/www/spec/jquery_couch_js_class_methods_spec.js
@@ -0,0 +1,523 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+// Specs for jquery_couch.js lines 48-156 and 415-448
+
+describe 'jQuery couchdb'
+ before
+ stubAlert();
+ end
+
+ after
+ destubAlert();
+ end
+
+ describe 'activeTasks'
+ before_each
+ db = $.couch.db("spec_db");
+ db.create();
+ end
+
+ after_each
+ db.drop();
+ end
+
+ it 'should return an empty array when there are no active tasks'
+ $.couch.activeTasks({
+ success: function(resp){
+ resp.should.eql []
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should return an active task'
+ // doing a bit of stuff here so compaction has something to do and takes a while
+ var battlestar, civillian;
+ db.saveDoc({"type":"Battlestar", "name":"Galactica"}, {
+ success: function(resp){
+ db.openDoc(resp.id, {
+ success: function(resp2){
+ battlestar = resp2;
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ battlestar.name = "Pegasus";
+ db.saveDoc(battlestar);
+
+ db.saveDoc({"type":"Civillian", "name":"Cloud 9"}, {
+ success: function(resp){
+ db.openDoc(resp.id, {
+ success: function(resp2){
+ civillian = resp2;
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ civillian.name = "Olympic Carrier";
+ db.saveDoc(civillian);
+ db.removeDoc(civillian);
+
+ db.compact({
+ ajaxStart: function(resp){
+ $.couch.activeTasks({
+ success: function(resp2){
+ resp2[0].type.should.eql "Database Compaction"
+ resp2[0].task.should.eql "spec_db"
+ resp2[0].should.have_prop "status"
+ resp2[0].should.include "pid"
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ }
+ });
+ end
+ end
+
+ describe 'allDbs'
+ it 'should return an array that includes a created database'
+ temp_db = new CouchDB("temp_spec_db", {"X-Couch-Full-Commit":"false"});
+ temp_db.createDb();
+ $.couch.allDbs({
+ success: function(resp){
+ resp.should.include "temp_spec_db"
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ temp_db.deleteDb();
+ end
+
+ it 'should return an array that does not include a database that does not exist'
+ $.couch.allDbs({
+ success: function(resp){
+ resp.should.not.include("not_existing_temp_spec_db");
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+ end
+
+ describe 'config'
+ it 'should get the config settings'
+ $.couch.config({
+ success: function(resp){
+ resp.httpd.port.should.eql window.location.port
+ resp.stats.samples.should.match /\[.*\]/
+ resp.native_query_servers.should.have_prop "erlang"
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should get a specific config setting'
+ $.couch.config({
+ success: function(resp){
+ parseInt(resp.max_document_size).should.be_a Number
+ resp.delayed_commits.should.be_a String
+ resp.database_dir.should.be_a String
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ }, "couchdb");
+ end
+
+ it 'should update a config setting'
+ $.couch.config({
+ success: function(resp){
+ resp.should.eql ""
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ }, "test", "colony", "Caprica");
+
+ $.couch.config({
+ success: function(resp){
+ resp.colony.should.eql "Caprica"
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ }, "test");
+
+ $.couch.config({}, "test", "colony", null);
+ end
+
+ it 'should delete a config setting'
+ $.couch.config({}, "test", "colony", "Caprica");
+
+ $.couch.config({
+ success: function(resp){
+ resp.should.eql "Caprica"
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ }, "test", "colony", null);
+
+ $.couch.config({
+ success: function(resp){
+ resp.should.eql {}
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ }, "test");
+ end
+
+ it 'should alert with an error message prefix'
+ $.couch.config("asdf", "asdf", "asdf");
+ alert_msg.should.match /An error occurred retrieving\/updating the server configuration/
+ end
+ end
+
+ describe 'session'
+ it 'should return information about the session'
+ $.couch.session({
+ success: function(resp){
+ resp.info.should.have_prop 'authentication_db'
+ resp.userCtx.should.include 'name'
+ resp.userCtx.roles.should.be_an Array
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+ end
+
+ describe 'userDb'
+ it 'should return the userDb'
+ var authentication_db;
+ $.couch.session({
+ success: function(resp){
+ authentication_db = resp.info.authentication_db;
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+
+ $.couch.userDb(function(resp){
+ resp.name.should.eql authentication_db
+ });
+ end
+
+ it 'should return a db instance'
+ $.couch.userDb(function(resp){
+ resp.should.respond_to 'allDocs'
+ resp.should.respond_to 'bulkSave'
+ });
+ end
+ end
+
+ describe 'user_db stuff'
+ before
+ useTestUserDb();
+ end
+
+ after
+ useOldUserDb();
+ end
+
+ describe 'signup'
+ it 'should return a saved user'
+ $.couch.signup(
+ {name: "Tom Zarek"}, "secretpass", {
+ success: function(resp){
+ resp.id.should.eql "org.couchdb.user:Tom Zarek"
+ resp.rev.length.should.be_at_least 30
+ resp.ok.should.be_true
+ users_db.deleteDoc({_id : resp.id, _rev : resp.rev})
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should create a userDoc in the user db'
+ $.couch.signup(
+ {name: "Tom Zarek"}, "secretpass", {
+ success: function(resp){
+ var user = users_db.open(resp.id);
+ user.name.should.eql "Tom Zarek"
+ user._id.should.eql "org.couchdb.user:Tom Zarek"
+ user.roles.should.eql []
+ user.password_sha.length.should.be_at_least 30
+ user.password_sha.should.be_a String
+ users_db.deleteDoc({_id : resp.id, _rev : resp.rev})
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should create a userDoc with roles when specified'
+ $.couch.signup(
+ {name: "Tom Zarek", roles: ["vice_president", "activist"]}, "secretpass", {
+ success: function(resp){
+ var user = users_db.open(resp.id);
+ user.roles.should.eql ["vice_president", "activist"]
+ users_db.deleteDoc({_id : resp.id, _rev : resp.rev})
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+ end
+
+ describe 'login'
+ before_each
+ user = {};
+ $.couch.signup({name: "Tom Zarek", roles: ["vice_president", "activist"]}, "secretpass", {
+ success: function(resp){
+ user.id = resp.id;
+ user.rev = resp.rev;
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ after_each
+ users_db.deleteDoc({_id : user.id, _rev : user.rev})
+ end
+
+ it 'should return the logged in user'
+ $.couch.login({
+ name: "Tom Zarek",
+ password: "secretpass",
+ success: function(resp){
+ resp.name.should.eql "Tom Zarek"
+ resp.ok.should.be_true
+ resp.roles.should.eql ["vice_president", "activist"]
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should result in a session for the logged in user'
+ $.couch.login({
+ name: "Tom Zarek",
+ password: "secretpass"
+ });
+ $.couch.session({
+ success: function(resp){
+ resp.info.authentication_db.should.eql "spec_users_db"
+ resp.userCtx.name.should.eql "Tom Zarek"
+ resp.userCtx.roles.should.eql ["vice_president", "activist"]
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should return a 404 when password is wrong'
+ $.couch.login({
+ name: "Tom Zarek",
+ password: "wrongpass",
+ error: function(status, error, reason){
+ status.should.eql 401
+ error.should.eql "unauthorized"
+ reason.should.eql "Name or password is incorrect."
+ },
+ success: function(resp){successCallback(resp)}
+ });
+ end
+
+ it 'should return a 404 when the user doesnt exist in the users db'
+ $.couch.login({
+ name: "Number Three",
+ password: "secretpass",
+ error: function(status, error, reason){
+ status.should.eql 401
+ error.should.eql "unauthorized"
+ reason.should.eql "Name or password is incorrect."
+ },
+ success: function(resp){successCallback(resp)}
+ });
+ end
+
+ it 'should alert with an error message prefix'
+ $.couch.login("asdf");
+ alert_msg.should.match /An error occurred logging in/
+ end
+ end
+
+ describe 'logout'
+ before_each
+ user = {};
+ $.couch.signup({name: "Tom Zarek", roles: ["vice_president", "activist"]}, "secretpass", {
+ success: function(resp){
+ user.id = resp.id;
+ user.rev = resp.rev;
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ $.couch.login({name: "Tom Zarek", password: "secretpass"});
+ end
+
+ after_each
+ users_db.deleteDoc({_id : user.id, _rev : user.rev})
+ end
+
+ it 'should return ok true'
+ $.couch.logout({
+ success: function(resp){
+ resp.ok.should.be_true
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should result in an empty session'
+ $.couch.logout();
+ $.couch.session({
+ success: function(resp){
+ resp.userCtx.name.should.be_null
+ resp.userCtx.roles.should.not.include ["vice_president"]
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+ end
+ end
+
+ describe 'encodeDocId'
+ it 'should return the encoded docID when it is not a design document'
+ $.couch.encodeDocId("viper").should.eql(encodeURIComponent("viper"))
+ end
+
+ it 'should encode only the name of the design document'
+ $.couch.encodeDocId("_design/raptor").should.eql("_design/" + encodeURIComponent("raptor"))
+ end
+
+ it 'should also work when the name of the des'
+ $.couch.encodeDocId("_design/battlestar/_view/crew").should.eql("_design/" + encodeURIComponent("battlestar/_view/crew"))
+ end
+ end
+
+ describe 'info'
+ it 'should return the CouchDB version'
+ $.couch.info({
+ success: function(resp){
+ resp.couchdb.should.eql "Welcome"
+ resp.version.should_match /^\d\d?\.\d\d?\.\d\d?.*/
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+ end
+
+ describe 'replicate'
+ before_each
+ db = $.couch.db("spec_db");
+ db.create();
+ db2 = $.couch.db("spec_db_2");
+ db2.create();
+ host = window.location.protocol + "//" + window.location.host ;
+ end
+
+ after_each
+ db.drop();
+ db2.drop();
+ end
+
+ it 'should return no_changes true when there are no changes between the dbs'
+ $.couch.replicate(host + db.uri, host + db2.uri, {
+ success: function(resp){
+ resp.ok.should.be_true
+ resp.no_changes.should.be_true
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should return the session ID'
+ db.saveDoc({'type':'battlestar', 'name':'galactica'});
+ $.couch.replicate(host + db.uri, host + db2.uri, {
+ success: function(resp){
+ resp.session_id.length.should.be_at_least 30
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should return source_last_seq'
+ db.saveDoc({'type':'battlestar', 'name':'galactica'});
+ db.saveDoc({'type':'battlestar', 'name':'pegasus'});
+
+ $.couch.replicate(host + db.uri, host + db2.uri, {
+ success: function(resp){
+ resp.source_last_seq.should.eql 2
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should return the replication history'
+ db.saveDoc({'type':'battlestar', 'name':'galactica'});
+ db.saveDoc({'type':'battlestar', 'name':'pegasus'});
+
+ $.couch.replicate(host + db.uri, host + db2.uri, {
+ success: function(resp){
+ resp.history[0].docs_written.should.eql 2
+ resp.history[0].start_last_seq.should.eql 0
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should pass through replication options'
+ db.saveDoc({'type':'battlestar', 'name':'galactica'});
+ db2.drop();
+ $.couch.replicate(host + db.uri, host + db2.uri, {
+ error: function(status, error, reason){
+ status.should.eql 500
+ reason.should.match /db_not_found/
+ },
+ success: function(resp){successCallback(resp)}
+ });
+
+ $.couch.replicate(host + db.uri, host + db2.uri, {
+ success: function(resp){
+ resp.ok.should.eql true
+ resp.history[0].docs_written.should.eql 1
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ }, {
+ "create_target":true
+ });
+
+ db2.info({
+ success: function(resp){
+ resp.db_name.should.eql "spec_db_2"
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should alert with an error message prefix'
+ $.couch.replicate("asdf");
+ alert_msg.should.match /Replication failed/
+ end
+ end
+
+ describe 'newUUID'
+ it 'should return a new UUID'
+ var new_uuid = $.couch.newUUID(1);
+ new_uuid.should.be_a String
+ new_uuid.should.have_length 32
+ end
+
+ it 'should fill the uuidCache with the specified number minus 1'
+ // we can't reach the uuidCache from here, so we mock the next request
+ // to test that the next uuid is not coming from the request, but from the cache.
+ $.couch.newUUID(2);
+ mock_request().and_return({'uuids':['a_sample_uuid']})
+ $.couch.newUUID(1).should.not.eql 'a_sample_uuid'
+ $.couch.newUUID(1).should.eql 'a_sample_uuid'
+ end
+
+ it 'should alert with an error message prefix'
+ $.couch.newUUID("asdf");
+ alert_msg.should.match /Failed to retrieve UUID batch/
+ end
+ end
+end \ No newline at end of file
diff --git a/rel/overlay/share/www/spec/jquery_couch_js_instance_methods_1_spec.js b/rel/overlay/share/www/spec/jquery_couch_js_instance_methods_1_spec.js
new file mode 100644
index 00000000..8538c856
--- /dev/null
+++ b/rel/overlay/share/www/spec/jquery_couch_js_instance_methods_1_spec.js
@@ -0,0 +1,202 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+// Specs for jquery_couch.js lines 163-209
+
+describe 'jQuery couchdb db'
+ before
+ stubAlert();
+ end
+
+ after
+ destubAlert();
+ end
+
+ before_each
+ db = $.couch.db('spec_db');
+ end
+
+ describe 'constructor'
+ it 'should set the name'
+ db.name.should.eql 'spec_db'
+ end
+
+ it 'should set the uri'
+ db.uri.should.eql '/spec_db/'
+ end
+ end
+
+ describe 'triggering db functions'
+ before_each
+ db.create();
+ end
+
+ after_each
+ db.drop();
+ end
+
+ describe 'compact'
+ it 'should return ok true'
+ db.compact({
+ success: function(resp) {
+ resp.ok.should.be_true
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should trigger _compact'
+ db.compact({
+ success: function(resp, obj) {
+ obj.url.should.eql "/spec_db/_compact"
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+ end
+
+ describe 'viewCleanup'
+ it 'should return ok true'
+ db.viewCleanup({
+ success: function(resp) {
+ resp.ok.should.be_true
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should trigger _view_cleanup'
+ db.viewCleanup({
+ success: function(resp, obj) {
+ obj.url.should.eql "/spec_db/_view_cleanup"
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+ end
+
+ describe 'compactView'
+ before_each
+ var designDoc = {
+ "views" : {
+ "people" : {
+ "map" : "function(doc) { emit(doc._id, doc); }"
+ }
+ },
+ "_id" : "_design/myview"
+ };
+ db.saveDoc(designDoc);
+ db.saveDoc({"Name" : "Felix Gaeta", "_id" : "123"});
+ end
+
+ it 'should return ok true'
+ db.compactView("myview", {
+ success: function(resp) {
+ resp.ok.should.be_true
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should trigger _compact_view with the groupname'
+ db.compactView("myview", {
+ success: function(resp, obj) {
+ obj.url.should.eql "/spec_db/_compact/myview"
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should return raise a 404 error when the design name doesnt exist'
+ db.compactView("non_existing_design_name", {
+ error: function(status, error, reason){
+ status.should.eql 404
+ error.should.eql "not_found"
+ reason.should.eql "missing"
+ },
+ success: function(resp){successCallback(resp)}
+ });
+ end
+
+ it 'should alert with an error message prefix'
+ db.compactView("asdf");
+ alert_msg.should.match /The view could not be compacted/
+ end
+ end
+ end
+
+ describe 'create'
+ after_each
+ db.drop();
+ end
+
+ it 'should return ok true'
+ db.create({
+ success: function(resp) {
+ resp.ok.should.be_true
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should result in a created db'
+ db.create();
+ db.create({
+ error: function(status, error, reason){
+ status.should.eql 412
+ error.should.eql "file_exists"
+ reason.should.eql "The database could not be created, the file already exists."
+ },
+ success: function(resp){successCallback(resp)}
+ });
+ end
+
+ it 'should alert with an error message prefix'
+ db.create();
+ db.create();
+ alert_msg.should.match /The database could not be created/
+ end
+ end
+
+ describe 'drop'
+ before_each
+ db.create();
+ end
+
+ it 'should return ok true'
+ db.drop({
+ success: function(resp) {
+ resp.ok.should.be_true
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should result in a deleted db'
+ db.drop();
+ db.drop({
+ error: function(status, error, reason){
+ status.should.eql 404
+ error.should.eql "not_found"
+ reason.should.eql "missing"
+ },
+ success: function(resp){successCallback(resp)}
+ });
+ end
+
+ it 'should alert with an error message prefix'
+ db.drop();
+ db.drop();
+ alert_msg.should.match /The database could not be deleted/
+ end
+ end
+end \ No newline at end of file
diff --git a/rel/overlay/share/www/spec/jquery_couch_js_instance_methods_2_spec.js b/rel/overlay/share/www/spec/jquery_couch_js_instance_methods_2_spec.js
new file mode 100644
index 00000000..8f35affa
--- /dev/null
+++ b/rel/overlay/share/www/spec/jquery_couch_js_instance_methods_2_spec.js
@@ -0,0 +1,433 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+// Specs for jquery_couch.js lines 210-299
+
+describe 'jQuery couchdb db'
+ before
+ stubAlert();
+ end
+
+ after
+ destubAlert();
+ end
+
+ before_each
+ db = $.couch.db('spec_db');
+ db.create();
+ end
+
+ after_each
+ db.drop();
+ end
+
+ describe 'info'
+ before_each
+ result = {};
+ db.info({
+ success: function(resp) { result = resp; },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should return the name of the database'
+ result.db_name.should.eql "spec_db"
+ end
+
+ it 'should return the number of documents'
+ result.doc_count.should.eql 0
+ end
+
+ it 'should return the start time of the db instance'
+ result.instance_start_time.should.have_length 16
+ end
+ end
+
+ describe 'allDocs'
+ it 'should return no docs when there arent any'
+ db.allDocs({
+ success: function(resp) {
+ resp.total_rows.should.eql 0
+ resp.rows.should.eql []
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ describe 'with docs'
+ before_each
+ db.saveDoc({"Name" : "Felix Gaeta", "_id" : "123"});
+ db.saveDoc({"Name" : "Samuel T. Anders", "_id" : "456"});
+ end
+
+ it 'should return all docs'
+ db.allDocs({
+ success: function(resp) {
+ resp.total_rows.should.eql 2
+ resp.rows.should.have_length 2
+ resp.rows[0].id.should.eql "123"
+ resp.rows[0].key.should.eql "123"
+ resp.rows[0].value.rev.length.should.be_at_least 30
+ resp.rows[1].id.should.eql "456"
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should pass through the options'
+ db.allDocs({
+ "startkey": "123",
+ "limit": "1",
+ success: function(resp) {
+ resp.rows.should.have_length 1
+ resp.rows[0].id.should.eql "123"
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+ end
+ end
+
+ describe 'allDesignDocs'
+ it 'should return nothing when there arent any design docs'
+ db.saveDoc({"Name" : "Felix Gaeta", "_id" : "123"});
+ db.allDesignDocs({
+ success: function(resp) {
+ resp.rows.should.eql []
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should return all design docs'
+ var designDoc = {
+ "views" : {
+ "people" : {
+ "map" : "function(doc) { emit(doc._id, doc); }"
+ }
+ },
+ "_id" : "_design/spec_db"
+ };
+ db.saveDoc(designDoc);
+ db.saveDoc({"Name" : "Felix Gaeta", "_id" : "123"});
+
+ db.allDesignDocs({
+ success: function(resp) {
+ resp.total_rows.should.eql 2
+ resp.rows.should.have_length 1
+ resp.rows[0].id.should.eql "_design/spec_db"
+ resp.rows[0].key.should.eql "_design/spec_db"
+ resp.rows[0].value.rev.length.should.be_at_least 30
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+ end
+
+ describe 'allApps'
+ it 'should provide a custom function with appName, appPath and design document when there is an attachment with index.html'
+ var designDoc = {"_id" : "_design/with_attachments"};
+
+ designDoc._attachments = {
+ "index.html" : {
+ "content_type": "text\/html",
+ // this is "<html><p>Hi, here is index!</p></html>", base64 encoded
+ "data": "PGh0bWw+PHA+SGksIGhlcmUgaXMgaW5kZXghPC9wPjwvaHRtbD4="
+ }
+ };
+ db.saveDoc(designDoc);
+
+ db.allApps({
+ eachApp: function(appName, appPath, ddoc) {
+ appName.should.eql "with_attachments"
+ appPath.should.eql "/spec_db/_design/with_attachments/index.html"
+ ddoc._id.should.eql "_design/with_attachments"
+ ddoc._attachments["index.html"].content_type.should.eql "text/html"
+ ddoc._attachments["index.html"].length.should.eql "<html><p>Hi, here is index!</p></html>".length
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should provide a custom function with appName, appPath and design document when there is a couchapp with index file'
+ var designDoc = {"_id" : "_design/with_index"};
+ designDoc.couchapp = {
+ "index" : "cylon"
+ };
+ db.saveDoc(designDoc);
+
+ db.allApps({
+ eachApp: function(appName, appPath, ddoc) {
+ appName.should.eql "with_index"
+ appPath.should.eql "/spec_db/_design/with_index/cylon"
+ ddoc._id.should.eql "_design/with_index"
+ ddoc.couchapp.index.should.eql "cylon"
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should not call the eachApp function when there is neither index.html in _attachments nor a couchapp index file'
+ var designDoc = {"_id" : "_design/nothing"};
+ db.saveDoc(designDoc);
+
+ var eachApp_called = false;
+ db.allApps({
+ eachApp: function(appName, appPath, ddoc) {
+ eachApp_called = true;
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+
+ eachApp_called.should.be_false
+ end
+
+ it 'should alert with an error message prefix'
+ db.allApps();
+ alert_msg.should.match /Please provide an eachApp function for allApps()/
+ end
+ end
+
+ describe 'openDoc'
+ before_each
+ doc = {"Name" : "Louanne Katraine", "Callsign" : "Kat", "_id" : "123"};
+ db.saveDoc(doc);
+ end
+
+ it 'should open the document'
+ db.openDoc("123", {
+ success: function(resp){
+ resp.should.eql doc
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should raise a 404 error when there is no document with the given ID'
+ db.openDoc("non_existing", {
+ error: function(status, error, reason){
+ status.should.eql 404
+ error.should.eql "not_found"
+ reason.should.eql "missing"
+ },
+ success: function(resp){successCallback(resp)}
+ });
+ end
+
+ it 'should pass through the options'
+ doc.Name = "Sasha";
+ db.saveDoc(doc);
+ db.openDoc("123", {
+ revs: true,
+ success: function(resp){
+ resp._revisions.start.should.eql 2
+ resp._revisions.ids.should.have_length 2
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should alert with an error message prefix'
+ db.openDoc("asdf");
+ alert_msg.should.match /The document could not be retrieved/
+ end
+ end
+
+ describe 'saveDoc'
+ before_each
+ doc = {"Name" : "Kara Thrace", "Callsign" : "Starbuck"};
+ end
+
+ it 'should save the document'
+ db.saveDoc(doc, {
+ success: function(resp, status){
+ status.should.eql 201
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should return ok true'
+ db.saveDoc(doc, {
+ success: function(resp, status){
+ resp.ok.should.be_true
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should return ID and revision of the document'
+ db.saveDoc(doc, {
+ success: function(resp, status){
+ resp.id.should.be_a String
+ resp.id.should.have_length 32
+ resp.rev.should.be_a String
+ resp.rev.length.should.be_at_least 30
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should result in a saved document with generated ID'
+ db.saveDoc(doc, {
+ success: function(resp, status){
+ db.openDoc(resp.id, {
+ success: function(resp2){
+ resp2.Name.should.eql "Kara Thrace"
+ resp2.Callsign.should.eql "Starbuck"
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should save the document with the specified ID'
+ doc._id = "123";
+ db.saveDoc(doc, {
+ success: function(resp, status){
+ resp.id.should.eql "123"
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should pass through the options'
+ db.saveDoc(doc, {
+ "batch" : "ok",
+ success: function(resp, status){
+ // when using batch ok, couch sends a 202 status immediately
+ status.should.eql 202
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should alert with an error message prefix'
+ db.saveDoc("asdf");
+ alert_msg.should.match /The document could not be saved/
+ end
+ end
+
+ describe 'bulkSave'
+ before_each
+ doc = {"Name" : "Kara Thrace", "Callsign" : "Starbuck"};
+ doc2 = {"Name" : "Karl C. Agathon", "Callsign" : "Helo"};
+ doc3 = {"Name" : "Sharon Valerii", "Callsign" : "Boomer"};
+ docs = [doc, doc2, doc3];
+ end
+
+ it 'should save all documents'
+ db.bulkSave({"docs": docs});
+ db.allDocs({
+ success: function(resp) {
+ resp.total_rows.should.eql 3
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should result in saved documents'
+ doc3._id = "789";
+ db.bulkSave({"docs": [doc3]});
+
+ db.openDoc("789", {
+ success: function(resp){
+ resp.Name.should.eql "Sharon Valerii"
+ resp.Callsign.should.eql "Boomer"
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should return ID and revision of the documents'
+ db.bulkSave({"docs": docs},{
+ success: function(resp){
+ resp[0].id.should.be_a String
+ resp[0].id.should.have_length 32
+ resp[0].rev.should.be_a String
+ resp[0].rev.length.should.be_at_least 30
+ resp[1].id.should.be_a String
+ resp[1].id.should.have_length 32
+ resp[1].rev.should.be_a String
+ resp[1].rev.length.should.be_at_least 30
+ resp[2].id.should.be_a String
+ resp[2].id.should.have_length 32
+ resp[2].rev.should.be_a String
+ resp[2].rev.length.should.be_at_least 30
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should save the document with specified IDs'
+ doc._id = "123";
+ doc2._id = "456";
+ docs = [doc, doc2, doc3];
+
+ db.bulkSave({"docs": docs},{
+ success: function(resp){
+ resp[0].id.should.eql "123"
+ resp[1].id.should.eql "456"
+ resp[2].id.should.have_length 32
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should pass through the options'
+ // a lengthy way to test that a conflict can't be created with the
+ // all_or_nothing option set to false, but can be when it's true.
+
+ var old_doc = {"Name" : "Louanne Katraine", "Callsign" : "Kat", "_id" : "123"};
+ db.saveDoc(old_doc, {
+ success: function(resp){
+ old_doc._rev = resp.rev;
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+
+ var new_doc = {"Name" : "Sasha", "Callsign" : "Kat", "_id" : "123"};
+
+ db.bulkSave({"docs": [new_doc], "all_or_nothing": false}, {
+ success: function(resp){
+ resp[0].id.should.eql "123"
+ resp[0].error.should.eql "conflict"
+ resp[0].reason.should.eql "Document update conflict."
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+
+ db.bulkSave({"docs": [new_doc], "all_or_nothing": true}, {
+ success: function(resp){
+ resp[0].id.should.eql "123"
+ resp[0].rev.should.not.eql old_doc._rev
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+
+ db.openDoc("123", {
+ "conflicts": true,
+ success: function(resp){
+ resp._conflicts[0].should.eql old_doc._rev
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should alert with an error message prefix'
+ db.bulkSave("asdf");
+ alert_msg.should.match /The documents could not be saved/
+ end
+ end
+end \ No newline at end of file
diff --git a/rel/overlay/share/www/spec/jquery_couch_js_instance_methods_3_spec.js b/rel/overlay/share/www/spec/jquery_couch_js_instance_methods_3_spec.js
new file mode 100644
index 00000000..5d27d817
--- /dev/null
+++ b/rel/overlay/share/www/spec/jquery_couch_js_instance_methods_3_spec.js
@@ -0,0 +1,540 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+// Specs for jquery_couch.js lines 300-411
+
+describe 'jQuery couchdb db'
+ before
+ stubAlert();
+ end
+
+ after
+ destubAlert();
+ end
+
+ before_each
+ db = $.couch.db('spec_db');
+ db.create();
+ end
+
+ after_each
+ db.drop();
+ end
+
+ describe 'removeDoc'
+ before_each
+ doc = {"Name" : "Louanne Katraine", "Callsign" : "Kat", "_id" : "345"};
+ saved_doc = {};
+ db.saveDoc(doc, {
+ success: function(resp){
+ saved_doc = resp;
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should result in a deleted document'
+ db.removeDoc({_id : "345", _rev : saved_doc.rev}, {
+ success: function(resp){
+ db.openDoc("345", {
+ error: function(status, error, reason){
+ status.should.eql 404
+ error.should.eql "not_found"
+ reason.should.eql "deleted"
+ },
+ success: function(resp){successCallback(resp)}
+ });
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should return ok true, the ID and the revision of the deleted document'
+ db.removeDoc({_id : "345", _rev : saved_doc.rev}, {
+ success: function(resp){
+ resp.ok.should.be_true
+ resp.id.should.eql "345"
+ resp.rev.should.be_a String
+ resp.rev.length.should.be_at_least 30
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should record the revision in the deleted document'
+ db.removeDoc({_id : "345", _rev : saved_doc.rev}, {
+ success: function(resp){
+ db.openDoc("345", {
+ rev: resp.rev,
+ success: function(resp2){
+ resp2._rev.should.eql resp.rev
+ resp2._id.should.eql resp.id
+ resp2._deleted.should.be_true
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should alert with an error message prefix'
+ db.removeDoc({_id: "asdf"});
+ alert_msg.should.match /The document could not be deleted/
+ end
+ end
+
+ describe 'bulkRemove'
+ before_each
+ doc = {"Name" : "Kara Thrace", "Callsign" : "Starbuck", "_id" : "123"};
+ doc2 = {"Name" : "Karl C. Agathon", "Callsign" : "Helo", "_id" : "456"};
+ doc3 = {"Name" : "Sharon Valerii", "Callsign" : "Boomer", "_id" : "789"};
+ docs = [doc, doc2, doc3];
+
+ db.bulkSave({"docs": docs}, {
+ success: function(resp){
+ for (var i = 0; i < docs.length; i++) {
+ docs[i]._rev = resp[i].rev;
+ }
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should remove all documents specified'
+ db.bulkRemove({"docs": docs});
+ db.allDocs({
+ success: function(resp) {
+ resp.total_rows.should.eql 0
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should not remove documents that should not have been deleted'
+ db.bulkRemove({"docs": [doc3]});
+ db.allDocs({
+ success: function(resp) {
+ resp.total_rows.should.eql 2
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should result in deleted documents'
+ db.bulkRemove({"docs": docs}, {
+ success: function(resp){
+ db.openDoc("123", {
+ error: function(status, error, reason){
+ status.should.eql 404
+ error.should.eql "not_found"
+ reason.should.eql "deleted"
+ },
+ success: function(resp){successCallback(resp)}
+ });
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should return the ID and the revision of the deleted documents'
+ db.bulkRemove({"docs": docs}, {
+ success: function(resp){
+ resp[0].id.should.eql "123"
+ resp[0].rev.should.be_a String
+ resp[0].rev.length.should.be_at_least 30
+ resp[1].id.should.eql "456"
+ resp[1].rev.should.be_a String
+ resp[1].rev.length.should.be_at_least 30
+ resp[2].id.should.eql "789"
+ resp[2].rev.should.be_a String
+ resp[2].rev.length.should.be_at_least 30
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should record the revision in the deleted documents'
+ db.bulkRemove({"docs": docs}, {
+ success: function(resp){
+ db.openDoc("123", {
+ rev: resp[0].rev,
+ success: function(resp2){
+ resp2._rev.should.eql resp[0].rev
+ resp2._id.should.eql resp[0].id
+ resp2._deleted.should.be_true
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should alert with an error message prefix'
+ db.bulkRemove({docs: ["asdf"]});
+ alert_msg.should.match /The documents could not be deleted/
+ end
+ end
+
+ describe 'copyDoc'
+ before_each
+ doc = {"Name" : "Sharon Agathon", "Callsign" : "Athena", "_id" : "123"};
+ db.saveDoc(doc);
+ end
+
+ it 'should result in another document with same data and new id'
+ db.copyDoc("123", {
+ success: function(resp){
+ resp.id.should.eql "456"
+ resp.rev.length.should.be_at_least 30
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ }, {
+ headers: {"Destination":"456"}
+ });
+
+ db.openDoc("456", {
+ success: function(resp){
+ resp.Name.should.eql "Sharon Agathon"
+ resp.Callsign.should.eql "Athena"
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should throw an error when trying to overwrite a document without providing a revision'
+ doc2 = {"Name" : "Louanne Katraine", "Callsign" : "Kat", "_id" : "456"};
+ db.saveDoc(doc2);
+
+ db.copyDoc("123", {
+ error: function(status, error, reason){
+ status.should.eql 409
+ error.should.eql "conflict"
+ reason.should.eql "Document update conflict."
+ },
+ success: function(resp){successCallback(resp)}
+ }, {
+ headers: {"Destination":"456"}
+ });
+ end
+
+ it 'should overwrite a document with the correct revision'
+ doc2 = {"Name" : "Louanne Katraine", "Callsign" : "Kat", "_id" : "456"};
+ var doc2_rev;
+ db.saveDoc(doc2, {
+ success: function(resp){
+ doc2_rev = resp.rev;
+ }
+ });
+
+ db.copyDoc("123", {
+ success: function(resp){
+ resp.id.should.eql "456"
+ resp.rev.length.should.be_at_least 30
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ }, {
+ headers: {"Destination":"456?rev=" + doc2_rev}
+ });
+
+ db.openDoc("456", {
+ success: function(resp){
+ resp.Name.should.eql "Sharon Agathon"
+ resp.Callsign.should.eql "Athena"
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should alert with an error message prefix'
+ db.copyDoc("asdf", {}, {});
+ alert_msg.should.match /The document could not be copied/
+ end
+ end
+
+ describe 'query'
+ before_each
+ db.saveDoc({"Name" : "Cally Tyrol", "job" : "deckhand", "_id" : "789"});
+ db.saveDoc({"Name" : "Felix Gaeta", "job" : "officer", "_id" : "123"});
+ db.saveDoc({"Name" : "Samuel T. Anders", "job" : "pilot", "_id" : "456"});
+ map_function = "function(doc) { emit(doc._id, 1); }";
+ reduce_function = "function(key, values, rereduce) { return sum(values); }";
+ end
+
+ it 'should apply the map function'
+ db.query(map_function, null, null, {
+ success: function(resp){
+ resp.rows.should.have_length 3
+ resp.rows[0].id.should.eql "123"
+ resp.rows[0].key.should.eql "123"
+ resp.rows[0].value.should.eql 1
+ resp.rows[1].id.should.eql "456"
+ resp.rows[1].key.should.eql "456"
+ resp.rows[1].value.should.eql 1
+ resp.rows[2].id.should.eql "789"
+ resp.rows[2].key.should.eql "789"
+ resp.rows[2].value.should.eql 1
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should apply the reduce function'
+ db.query(map_function, reduce_function, null, {
+ success: function(resp){
+ resp.rows.should.have_length 1
+ resp.rows[0].key.should.be_null
+ resp.rows[0].value.should_eql 3
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should pass through the options'
+ db.query(map_function, null, null, {
+ "startkey": "456",
+ success: function(resp){
+ resp.rows.should.have_length 2
+ resp.rows[0].id.should.eql "456"
+ resp.rows[0].key.should.eql "456"
+ resp.rows[0].value.should.eql 1
+ resp.rows[1].id.should.eql "789"
+ resp.rows[1].key.should.eql "789"
+ resp.rows[1].value.should.eql 1
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should pass through the keys'
+ //shouldn't this better work? TODO: implement in jquery.couch.js
+ console.log("shouldn't this better work? TODO: implement in jquery.couch.js")
+ db.query(map_function, null, null, {
+ "keys": ["456", "123"],
+ success: function(resp){
+ resp.rows.should.have_length 2
+ resp.rows[0].id.should.eql "456"
+ resp.rows[0].key.should.eql "456"
+ resp.rows[0].value.should.eql 1
+ resp.rows[1].id.should.eql "123"
+ resp.rows[1].key.should.eql "123"
+ resp.rows[1].value.should.eql 1
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should pass through the options and the keys'
+ //shouldn't this better work? TODO: implement in jquery.couch.js
+ console.log("shouldn't this better work? TODO: implement in jquery.couch.js")
+ db.query(map_function, null, null, {
+ "include_docs":"true",
+ "keys": ["456"],
+ success: function(resp){
+ resp.rows.should.have_length 1
+ resp.rows[0].id.should.eql "456"
+ resp.rows[0].key.should.eql "456"
+ resp.rows[0].value.should.eql 1
+ resp.rows[0].doc["job"].should.eql "pilot"
+ resp.rows[0].doc["_rev"].length.should.be_at_least 30
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should apply a query in erlang also'
+ // when this test fails, read this: http://wiki.apache.org/couchdb/EnableErlangViews
+ var erlang_map = 'fun({Doc}) -> ' +
+ 'ID = proplists:get_value(<<"_id">>, Doc, null), ' +
+ 'Emit(ID, 1) ' +
+ 'end.';
+ db.query(erlang_map, null, "erlang", {
+ success: function(resp){
+ resp.rows.should.have_length 3
+ resp.rows[0].id.should.eql "123"
+ resp.rows[0].key.should.eql "123"
+ resp.rows[0].value.should.eql 1
+ resp.rows[1].id.should.eql "456"
+ resp.rows[1].key.should.eql "456"
+ resp.rows[1].value.should.eql 1
+ resp.rows[2].id.should.eql "789"
+ resp.rows[2].key.should.eql "789"
+ resp.rows[2].value.should.eql 1
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should alert with an error message prefix'
+ db.query("asdf");
+ alert_msg.should.match /An error occurred querying the database/
+ end
+ end
+
+ describe 'view'
+ before_each
+ db.saveDoc({"Name" : "Cally Tyrol", "job" : "deckhand", "_id" : "789"});
+ db.saveDoc({"Name" : "Felix Gaeta", "job" : "officer", "_id" : "123"});
+ db.saveDoc({"Name" : "Samuel T. Anders", "job" : "pilot", "_id" : "456"});
+ view = {
+ "views" : {
+ "people" : {
+ "map" : "function(doc) { emit(doc._id, doc.Name); }"
+ }
+ },
+ "_id" : "_design/spec_db"
+ };
+ db.saveDoc(view);
+ end
+
+ it 'should apply the view'
+ db.view('spec_db/people', {
+ success: function(resp){
+ resp.rows.should.have_length 3
+ resp.rows[0].id.should.eql "123"
+ resp.rows[0].key.should.eql "123"
+ resp.rows[0].value.should.eql "Felix Gaeta"
+ resp.rows[1].id.should.eql "456"
+ resp.rows[1].key.should.eql "456"
+ resp.rows[1].value.should.eql "Samuel T. Anders"
+ resp.rows[2].id.should.eql "789"
+ resp.rows[2].key.should.eql "789"
+ resp.rows[2].value.should.eql "Cally Tyrol"
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should pass through the options'
+ db.view('spec_db/people', {
+ "skip":"2",
+ success: function(resp){
+ resp.rows.should.have_length 1
+ resp.rows[0].id.should.eql "789"
+ resp.rows[0].key.should.eql "789"
+ resp.rows[0].value.should.eql "Cally Tyrol"
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should pass through the keys'
+ db.view('spec_db/people', {
+ "keys":["456", "123"],
+ success: function(resp){
+ resp.rows.should.have_length 2
+ resp.rows[0].id.should.eql "456"
+ resp.rows[0].key.should.eql "456"
+ resp.rows[0].value.should.eql "Samuel T. Anders"
+ resp.rows[1].id.should.eql "123"
+ resp.rows[1].key.should.eql "123"
+ resp.rows[1].value.should.eql "Felix Gaeta"
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should pass through the options and the keys'
+ db.view('spec_db/people', {
+ "include_docs":"true",
+ "keys":["456"],
+ success: function(resp){
+ resp.rows.should.have_length 1
+ resp.rows[0].id.should.eql "456"
+ resp.rows[0].key.should.eql "456"
+ resp.rows[0].value.should.eql "Samuel T. Anders"
+ resp.rows[0].doc["job"].should.eql "pilot"
+ resp.rows[0].doc["_rev"].length.should.be_at_least 30
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should throw a 404 when the view doesnt exist'
+ db.view('spec_db/non_existing_view', {
+ error: function(status, error, reason){
+ status.should.eql 404
+ error.should.eql "not_found"
+ reason.should.eql "missing_named_view"
+ },
+ success: function(resp){successCallback(resp)}
+ });
+ end
+
+ it 'should alert with an error message prefix'
+ db.view("asdf");
+ alert_msg.should.match /An error occurred accessing the view/
+ end
+ end
+
+ describe 'setDbProperty'
+ it 'should return ok true'
+ db.setDbProperty("_revs_limit", 1500, {
+ success: function(resp){
+ resp.ok.should.be_true
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should set a db property'
+ db.setDbProperty("_revs_limit", 1500);
+ db.getDbProperty("_revs_limit", {
+ success: function(resp){
+ resp.should.eql 1500
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ db.setDbProperty("_revs_limit", 1200);
+ db.getDbProperty("_revs_limit", {
+ success: function(resp){
+ resp.should.eql 1200
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should alert with an error message prefix'
+ db.setDbProperty("asdf");
+ alert_msg.should.match /The property could not be updated/
+ end
+ end
+
+ describe 'getDbProperty'
+ it 'should get a db property'
+ db.setDbProperty("_revs_limit", 1200);
+ db.getDbProperty("_revs_limit", {
+ success: function(resp){
+ resp.should.eql 1200
+ },
+ error: function(status, error, reason){errorCallback(status, error, reason)}
+ });
+ end
+
+ it 'should throw a 404 when the property doesnt exist'
+ db.getDbProperty("_doesnt_exist", {
+ error: function(status, error, reason){
+ status.should.eql 404
+ error.should.eql "not_found"
+ reason.should.eql "missing"
+ },
+ success: function(resp){successCallback(resp)}
+ });
+ end
+
+ it 'should alert with an error message prefix'
+ db.getDbProperty("asdf");
+ alert_msg.should.match /The property could not be retrieved/
+ end
+ end
+end \ No newline at end of file
diff --git a/rel/overlay/share/www/spec/run.html b/rel/overlay/share/www/spec/run.html
new file mode 100644
index 00000000..e438333d
--- /dev/null
+++ b/rel/overlay/share/www/spec/run.html
@@ -0,0 +1,46 @@
+<!--
+Licensed under the Apache License, Version 2.0 (the "License"); you may not
+use this file except in compliance with the License. You may obtain a copy of
+the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+License for the specific language governing permissions and limitations under
+the License.
+-->
+<html>
+ <head>
+ <link type="text/css" rel="stylesheet" href="../script/jspec/jspec.css" />
+ <script src="../script/jquery.js"></script>
+ <script src="../script/sha1.js"></script>
+ <script src="../script/jspec/jspec.js"></script>
+ <script src="../script/jspec/jspec.jquery.js"></script>
+ <script src="../script/jspec/jspec.xhr.js"></script>
+ <script src="./custom_helpers.js"></script>
+ <script src="../script/couch.js"></script>
+ <script src="../script/jquery.couch.js"></script>
+ <script>
+ function runSuites() {
+ JSpec
+ .exec('couch_js_class_methods_spec.js')
+ .exec('couch_js_instance_methods_1_spec.js')
+ .exec('couch_js_instance_methods_2_spec.js')
+ .exec('couch_js_instance_methods_3_spec.js')
+ .exec('jquery_couch_js_class_methods_spec.js')
+ .exec('jquery_couch_js_instance_methods_1_spec.js')
+ .exec('jquery_couch_js_instance_methods_2_spec.js')
+ .exec('jquery_couch_js_instance_methods_3_spec.js')
+ .run({failuresOnly: true})
+ .report()
+ }
+ </script>
+ </head>
+ <body class="jspec" onLoad="runSuites();">
+ <div id="jspec-top"><h2 id="jspec-title">JSpec <em><script>document.write(JSpec.version)</script></em></h2></div>
+ <div id="jspec"></div>
+ <div id="jspec-bottom"></div>
+ </body>
+</html>