diff --git a/.gitignore b/.gitignore
index 0e6c4d9..0937b67 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,9 @@
-dist/*
-.tmp
-test/keen-unit-all.js
-node_modules
-bower_components
*.log
.DS_Store
+.tmp
+.awspublish-*
+dist/*
+bower_components
+node_modules
+test/keen-unit-all.js
+test/unit/build/*
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..3f6b437
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,16 @@
+.bowerrc
+.DS_Store
+.git*
+*.log
+*.md
+*.yml
+.tmp*
+.awspublish-*
+
+bower.json
+gulpfile.js
+karma.conf.js
+
+docs
+dist
+test
diff --git a/.travis.yml b/.travis.yml
index 13da1b4..f60c5c3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,12 +1,9 @@
language: node_js
-
node_js:
- "0.10"
-
branches:
only:
- master
-
env:
global:
- secure: |-
@@ -17,19 +14,10 @@ env:
AjvRPOjPEuDl8qwEUOk2V1HH6C5AoE8V0/1+twsnaEcgfHWGDk3g7oPfMOW3
ArybZFvN+wpvWfwoJAhRV7yQv9yoGT1fEtMtn3GB9mQiCFO70L/MrqXePJAr
ajs3LH0wqhj1zW8UUmhTxBSLvzRyW++W5yx2hYr92EkrlpFZZUg=
- - LOGS_DIR=/tmp/keen-build/logs
-
install:
- - npm install -g grunt-cli
+ - npm install -g browserify
- npm install -g bower
- npm install
- bower install
-
-before_script:
- - mkdir -p $LOGS_DIR
- - chmod +x ./config/sauce_connect_setup.sh
- - ./config/sauce_connect_setup.sh
-
notifications:
- email:
- on_success: never
+ email: never
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..857e73c
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,204 @@
+
+
+
+# 3.2.0 keen-js learns to node.js (2014-12-28)
+
+This library now runs in the browser and the server, and can be installed via npm: `npm install keen-js`
+
+## New to keen-js
+
+* 'client.addEvents' - record multiple events with a single API call (#108)
+* 'client.addEvent("collection", data, null, false)' - fourth argument of `null` invokes a synchronous XHR call, which is useful for sending events before a page unloads (#188)
+* 'client.get' - execute a generic GET request (New approach to #139)
+* 'client.put' - execute a generic PUT request
+* 'client.post' - execute a generic POST request
+* 'client.del' - execute a generic DELETE request (server-only)
+* `Keen.utils.encryptScopedKeys` - create a new scoped key
+* `Keen.utils.decryptScopedKeys` - decrypt an existing scoped key
+* `Keen.noConflict` – avoid version collisions (#159)
+* `Keen.Request` object supports timeouts (explained below), and must be explicitly run with `.refresh()` (#209, wip)
+* "Metric" visualization shows raw value via the HTML element's `title` attribute (#206, wip)
+
+```javascript
+var req = new Keen.Request(client, [query1, query2], callback)
+ .timeout(300*1000)
+ .refresh();
+```
+
+## Keen.Visualization has been removed
+
+This object doesn't add any value, and only creates more surface-area for new users to reason about.
+
+Check out `Keen.Dataviz` for building customized visualizations. The docs have been updated to reflect this object's departure, and .run still works as expected.
+
+## Breaking change to all callback signatures
+
+All callbacks now use the single function `(err, res)` callback pattern common to node.js. This was done to make implementations portable between the browser and the server.
+
+This:
+
+```javascript
+client.run(query, function(res){
+ // handle response
+}, function(err){
+ // handle error
+});
+```
+
+Now looks like this:
+
+```javascript
+client.run(query, function(err, res){
+ // if (err) handle err
+ // handle response
+});
+```
+
+## Build/test-related
+
+* Switched from [grunt](http://gruntjs.com/) to [gulp](http://gulpjs.com/)
+* [Browserify](http://browserify.org/) builds browser-specific versions of the library
+* [Karma](http://karma-runner.github.io/) runs tests on local browsers (Chrome, FF, Safari)
+* [SauceLabs testing](https://saucelabs.com/u/keenlabs) tests IE + mobile
+
+
+
+# 3.1.0 Visualization Reboot (2014-11-03)
+
+Complete rewrite of `Keen.Visualization`, resulting in two brand new tools for creating dynamic, highly-customizable data visualizations.
+
+## [Dataviz](https://github.com/keenlabs/keen-js/tree/master/src/dataviz)
+
+* Composable interface for on-the-fly modifications of [pretty much everything](https://github.com/keenlabs/keen-js/tree/master/src/dataviz)
+* Improved memory management, smaller footprint, better performance
+* Charts render a million times faster (not benchmarked, but srsly, it's silly)
+* Two new chart library adapters: [Chart.js](http://www.chartjs.org/) and [C3.js](http://c3js.org/). Either can be configured as the default
+* Expanded color palette with 18 light+dark variants: now at 27 colors!
+* Explicit methods for [handling different types of data](https://github.com/keenlabs/keen-js/tree/clean-viz/src/dataviz#data-handling)
+* Explicit methods for updating charts, with full control of "loading" state spinner
+* Display custom error messages
+* Determine if intervals should be indexed by "timeframe.start" or "timeframe.end"
+* Sort both groups and intervals, ascending or descending
+
+## [Dataset](https://github.com/keenlabs/keen-js/tree/master/src/dataset)
+
+Dataset is an abstraction layer that handles data inside of `Keen.Dataviz`. This tool unpacks arbitrary JSON into a 2-dimensional array (a table), and offers a set of simple but powerful tools for modifying, filtering and sorting that data. `Keen.Dataviz` charting adapters all know how to unpack this data format into their own crazy needs.
+
+I'll build out a series of demos that showcase what this tool can do, but here are a few examples that you can accomplish with a few (like one or two) lines of javascript:
+
+* For a group_by + interval query:
+* Insert a new series that shows the median (sum/min/max/etc) value of all data points at each interval
+* Sort groups bases the those same results (sort by sum of each series)
+* Remove all records/series that fall below a given threshold (only show the top N results)
+* Multiply all results by 100, or any number
+* Join multiple queries into one chart
+
+The big idea here is that it takes something that once took dozens or hundreds of lines of loopy JS magic and turns it into a quick, repeatable, easily-customizable use of a documented feature.
+
+## Test Coverage
+
+We added over **150 new tests** to cover these two classes, and have expanded our test coverage to include iOS (6-7.1), Android (4.1-4.4), and Chrome/Firefox Beta builds.
+
+## Optimizations
+
+Disable sending of events by setting `Keen.enabled = false;`. This is handy for disabling event recording from local or development environments.
+
+Refactored our [loading script](https://gist.github.com/dustinlarimer/19fedf00c44d120ef8b4), makes it easy to stay up to date with the latest version. Now, when users see the JS blob for embedding our library in the page, it'll look like this:
+
+```javascript
+!function(i,o){i("Keen","//d26b395fwzu5fz.cloudfront.net/3.1.0/keen.min.js",o)}(function(a,b,c){var d,e,f;c["_"+a]={},c[a]=function(b){c["_"+a].clients=c["_"+a].clients||{},c["_"+a].clients[b.projectId]=this,this._config=b},c[a].ready=function(b){c["_"+a].ready=c["_"+a].ready||[],c["_"+a].ready.push(b)},d=["addEvent","setGlobalProperties","trackExternalLink","on"];for(var g=0;g
+# 3.0.9 Improved request handling (2014-10-01)
+
+This update greatly improves request handling for sending and querying events.
+
+If JSONP is selected and the produced URL exceeds a given threshold, the library attempts to use XHR/POST if supported.
+
+We have also added more robust support for XHR in older versions of IE, and expanded test coverage to include iOS (6-7.1), Android (4.1-4.4), and Chrome/Firefox Beta builds.
+
+
+# 3.0.8 Improved memory management for Keen.Visualization (2014-08-18)
+
+Call .remove() or .trigger("remove") on a chart for proper disposal.
+
+
+# 3.0.7 Bug fixes, timezone fixes, query refreshing (2014-08-06)
+
+__No notes, sorry__
+
+
+# 3.0.6 [YANKED]
+
+
+# 3.0.5 AMD Support
+
+This release refactors internal dependency placement to allow for easy use with RequireJS. The library is loaded with an explicitly named module ID ("keen"), which presents a light configuration step, but prevents anonymous define() mismatch mayhem.
+
+Example implementation:
+
+```javascript
+requirejs.config({
+ paths: {
+ "keen": "http://d26b395fwzu5fz.cloudfront.net/3.0.5/keen.js"
+ }
+});
+require([ "keen" ], function(Keen) {
+ var client = new Keen({ ... });
+});
+```
+
+
+# 3.0.4 Important patch for IE11
+
+Fixed a type-check method that caused queries to fail in IE11 (#96).
+
+
+# 3.0.3 Visual Error Messages
+
+client.draw() now visualizes API response messages when errors occur.
+
+
+# 3.0.2 Fixed trackExternalLink and data sorting
+
+Fixed an issue with trackExternalLink ignoring `target="_blank"` attributes, and failing to execute when anchor tags have nested DOM elements like `` tags.
+
+Applied column sorting to static group-by queries, for better arrangment in pie and bar charts.
+
+Fixed #73, #90 and #92
+
+
+# 3.0.1 Visualization bug fixes and enhancements
+
+* Keen.Metric now supports chartOptions.prefix, chartOptions.suffix, and has more stable colors handling.
+* Grouped interval responses are presented with a multi-line chart now.
+* Changed chartType: "datatable" to table. datatable was left behind from an internal project, but breaks the naming pattern we want to establish with underlying libraries.
+* Expanded color palette
+
+
+
+Resolves #72, #74, #76, #80, #82, #85, #86, #87
+
+
+# 3.0.0 Hello, world
+
+Initial re-release of keen-js!
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 1ee1123..296a387 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -8,12 +8,15 @@ $ npm install
$ bower install
# Build and launch project site
-$ grunt dev
+$ gulp
+
+# Build and launch with tests
+$ gulp with-tests
# View test results at http://localhost:9999
```
-## Pull Request Template
+## Pull Request Template
Please use the PR template below.
@@ -26,7 +29,7 @@ Please use the PR template below.
#### How should this be manually tested? (if appropriate)
-* run `grunt dev`
+* run `gulp with-tests`
* tests run at `localhost:9999/`
#### Are there any related issues open?
diff --git a/Gruntfile.js b/Gruntfile.js
deleted file mode 100644
index 3492071..0000000
--- a/Gruntfile.js
+++ /dev/null
@@ -1,240 +0,0 @@
-var saucelabs = require('./config/saucelabs')(),
- aws = require('./config/aws')(),
- wraps = require('./config/wrappers')();
-
-module.exports = function(grunt) {
-
- grunt.loadNpmTasks("grunt-contrib-connect");
- grunt.loadNpmTasks("grunt-contrib-concat");
- grunt.loadNpmTasks("grunt-contrib-watch");
- grunt.loadNpmTasks('grunt-s3');
- grunt.loadNpmTasks('grunt-yui-compressor');
-
- grunt.loadNpmTasks('grunt-saucelabs');
-
- grunt.initConfig({
- pkg: grunt.file.readJSON("package.json"),
-
- concat: {
-
- options: {
- stripBanners: {
- block: true,
- line: true
- },
- process: function(src, filepath) {
- var namespace = (grunt.option("namespace") || false);
- src = ((namespace) ? src.replace("'Keen'", "'" + namespace + "'") : src);
- src = src.replace("BUILD_VERSION", grunt.file.readJSON("package.json")["version"]);
- return "// Source: " + filepath + "\n" + src;
- }
- },
-
- // Assemble Keen (core)
- core: {
- src: [
- "src/core/index.js"
- , "src/core/events.js"
- , "src/core/async.js"
- , "src/core/**/*.js"
- ],
- dest: ".tmp/core.js"
- },
-
- // Assemble Keen.Dataset
- dataset: {
- src: [
- "src/dataset/index.js",
- "src/dataset/**/*.js"
- ],
- dest: ".tmp/dataset.js"
- },
-
- // Assemble Keen.Dataviz
- dataviz: {
- src: [
- "src/dataviz/index.js",
- "src/dataviz/**/*.js"
- ],
- dest: ".tmp/dataviz.js"
- },
-
- // Assemble keen.js (full)
- all: {
- options: {
- // Library Banner/Footer (makes happy AMD modules)
- banner: wraps.libraryBanner,
- footer: wraps.libraryFooter
- },
- src: [
- ".tmp/core.js"
- , "src/query.js"
-
- , "src/utils/base64.js"
- , "src/utils/json2.js"
- , "src/utils/keen-domready.js"
- , "src/utils/keen-spinner.js"
-
- , ".tmp/dataset.js"
- , ".tmp/dataviz.js"
- , "src/visualization.js"
- ],
- dest: "dist/<%= pkg.name %>.js"
- },
-
- // Assemble keen-tracking.js
- tracker: {
- options: {
- banner: wraps.libraryBanner,
- footer: wraps.libraryFooter
- },
- src: [
- ".tmp/core.js"
- , "src/utils/base64.js"
- , "src/utils/json2.js"
- , "src/utils/keen-domready.js"
- ],
- dest: "dist/<%= pkg.name %>-tracker.js"
- },
-
- // Build adapters as stand-alone modules
- adapters: {
- options: {
- // Adapter Banner/Footer (makes happy AMD modules)
- banner: wraps.adapterBanner,
- footer: wraps.adapterFooter
- },
- files: {
- "dist/adapters/keen-adapter-google.js" : ["src/dataviz/adapters/google.js"],
- "dist/adapters/keen-adapter-chartjs.js" : ["src/dataviz/adapters/chartjs.js"],
- "dist/adapters/keen-adapter-c3.js" : ["src/dataviz/adapters/c3.js"]
- }
- },
-
- // Build unit tests
- test: {
- src: [
- "test/unit/core.js"
- , "test/unit/track.js"
- , "test/unit/query.js"
- , "test/unit/dataviz.js"
- , "test/unit/dataset.js"
- , "test/unit/visualization.js"
- , "test/unit/utils.js"
- , "test/unit/data/**/*.js"
- ],
- dest: "test/keen-unit-all.js"
- },
-
- loader: {
- src: "src/loader.js",
- dest: "dist/<%= pkg.name %>-loader.js"
- }
- },
-
- min: {
- "keen": {
- src: "dist/<%= pkg.name %>.js",
- dest: "dist/<%= pkg.name %>.min.js"
- },
- "keen-tracker": {
- src: "dist/<%= pkg.name %>-tracker.js",
- dest: "dist/<%= pkg.name %>-tracker.min.js"
- },
- "keen-loader": {
- src: "dist/<%= pkg.name %>-loader.js",
- dest: "dist/<%= pkg.name %>-loader.min.js"
- }
- },
-
- watch: {
- javascript: {
- files: "src/**/*.js",
- tasks: [ "build" ]
- },
- tests: {
- files: "test/unit/**/*.js",
- tasks: [ "build" ]
- }
- },
-
- connect: {
- server: {
- options: {
- base: 'test',
- port: 9999
- }
- }
- },
-
- 'saucelabs-mocha': {
- all: {
- options: {
- 'testname': new Date().toISOString(),
- 'username': saucelabs.username,
- 'key': saucelabs.key,
- 'build': saucelabs.buildID,
- 'urls': saucelabs.urls,
- 'browsers': saucelabs.browsers,
- 'concurrency': saucelabs.concurrency,
- 'maxRetries': saucelabs.maxRetries,
- 'max-duration': saucelabs.maxDuration,
- 'sauceConfig': saucelabs.additionalConfig
- }
- }
- },
-
- s3: {
- options: {
- key: aws.key,
- secret: aws.secret,
- bucket: aws.bucket,
- access: 'public-read',
- headers: {
- // Two Year cache policy (1000 * 60 * 60 * 1) // 1 hour
- "Cache-Control": "max-age=3600000, public",
- "Expires": new Date(Date.now() + 3600000).toUTCString()
- },
- gzip: true
- },
- release: {
- upload: [
- // {
- // src: 'dist/*',
- // dest: 'latest/'
- // },
- {
- src: 'dist/*',
- dest: '<%= pkg.version %>/'
- }
- ]/*,
- sync: [
- {
- src: 'dist/*',
- dest: '<%= pkg.version %>/'
- }
- ]*/
- },
- staging: {
- upload: [
- {
- src: 'dist/*',
- dest: 'staging/',
- options: {
- headers: {
- "Cache-Control": "max-age=1000, public",
- "Expires": new Date(Date.now() + 1000).toUTCString()
- }
- }
- }
- ]
- }
- }
-
- });
-
- grunt.registerTask('build', ['concat', 'min']); // uglify
- grunt.registerTask('dev', ['build', 'connect', 'watch']);
- grunt.registerTask('test', ['build', 'connect', 'saucelabs-mocha']);
- grunt.registerTask('default', ['build']);
-};
diff --git a/README.md b/README.md
index 71e9de6..e6d9c5b 100644
--- a/README.md
+++ b/README.md
@@ -1,19 +1,17 @@
-# Keen IO JavaScript SDK (v3.1.0)
+# Keen IO JavaScript SDK (v3.2.0)
-**Important:** v3 is now the current version. v2 source and docs are located [here](https://github.com/keenlabs/keen-js/tree/v2).
-
-
-_Tracking is manually tested and validated on Internet Explorer 6, 7, and 8._
+**Important:** v3.2.0 introduced several breaking changes from previous versions. Check out the [Changelog](./Changelog.md#3.2.0) before upgrading.
+
## Documentation
-Docs have been moved into the master branch, so they stay in sync with each release.
+Docs have been moved into the master branch, so they stay in sync with each release. Check out the latest [here](./docs).
+
+v3.1.0 docs are [here](https://github.com/keen/keen-js/tree/v3.1.0/docs), and v2 docs are [way back here](https://github.com/keen/keen-js/tree/v2).
-Check out the latest [here](./docs).
## Get Project ID & API Keys
@@ -22,18 +20,26 @@ If you haven’t done so already, [login to Keen IO to create a project](https:/
## Install the library
-Load this library asynchronously from our CDN by copy/pasting this snippet of JavaScript above the `` tag of your page.
+```ssh
+# via npm
+$ npm install keen-js
+
+# or bower
+$ bower install keen-js
+```
+
+For quick browser use, copy/paste this snippet of JavaScript above the `` tag of your page:
```html
```
-Alternatively, you can load the library synchronously from our CDN:
+Or load the library synchronously from our CDN:
```html
-
+
```
Read our [Installation guide](./docs/installation.md) to learn about all the ways this library can fit into your workflow.
@@ -46,25 +52,28 @@ When instantiating a new Keen JS client, there are a number of possible configur
```html
```
You can configure new instances for as many projects as necessary.
-## Tracking Events
+## Record a single event
-Let's record some data! Here is a basic example for tracking events in your app:
+Here is a basic example for tracking "purchases" in your app:
-``` javascript
+```javascript
// Configure an instance for your project
-var client = new Keen({...});
+var client = new Keen({
+ projectId: "YOUR_PROJECT_ID",
+ writeKey: "YOUR_WRITE_KEY"
+});
// Create a data object with the properties you want to send
var purchase = {
@@ -82,9 +91,31 @@ client.addEvent("purchases", purchase);
Send as many events as you like. Each event will be fired off to the Keen IO servers asynchronously.
+## Record multiple events
+
+```javascript
+// Configure an instance for your project
+var client = new Keen({...});
+
+// Send multiple events to several collections
+client.addEvents({
+ "purchases": [
+ { item: "golden gadget", price: 25.50, transaction_id: "f029342" },
+ { item: "a different gadget", price: 17.75, transaction_id: "f029342" }
+ ],
+ "transactions": [
+ {
+ id: "f029342",
+ items: 2,
+ total: 43.25
+ }
+ ]
+});
+```
+
Read more about all the ways you can track events in our [tracking guide](./docs/track.md).
-Wondering what else you should track? Browse our [data modeling guide](https://github.com/keenlabs/data-modeling-guide), and send us recommendations or pull requests if you don't find what you're looking for.
+Wondering what else you should track? Browse our [data modeling guide](https://github.com/keen/data-modeling-guide), and send us recommendations or pull requests if you don't find what you're looking for.
## Querying events
@@ -101,8 +132,8 @@ var your_analysis = new Keen.Query(analysisType, params);
```javascript
var client = new Keen({
- projectId: "your_project_id",
- readKey: "your_read_key"
+ projectId: "YOUR_PROJECT_ID",
+ readKey: "YOUR_READ_KEY"
});
var count = new Keen.Query("count", {
@@ -112,8 +143,9 @@ var count = new Keen.Query("count", {
});
// Send query
-client.run(count, function(response){
- // response.result
+client.run(count, function(err, res){
+ // if (err) handle error
+ console.log(res.result);
});
```
@@ -148,9 +180,15 @@ client.draw(count, document.getElementById("chart-wrapper"), {
Read more about building charts from query responses in our [visualization guide](./docs/visualization.md).
+## Contributing
+
+This is an open source project and we love involvement from the community! Hit us up with pull requests and issues.
+
+The aim is to build up this module to completely represent the API provided by Keen IO, which is quite extensive. The more contributions the better!
+
## Resources
-[Data Modeling Guide](https://api.keen.io/3.0/projects/5337e28273f4bb4499000000/events/click?api_key=a0bb828de21e953a675610cb6e6b8935537b19c2f0ac33937d6d1df2cc8fddbf379a81ad398618897b70d15c6b42647c3e063a689bc367f5c32b66c18010541c0ad1cf3dbd36100dc4475c306b238cb6f5b05f082dc4071e35094a722b1f3e29fad63c933ea8e33e8b892c770df5e1bb&data=eyJwYWdlIjogIkRhdGEgTW9kZWxpbmcgR3VpZGUiLCJyZWZlcnJlciI6ICJSRUFETUUubWQifQ==&redirect=https://github.com/keenlabs/data-modeling-guide/)
+[Data Modeling Guide](https://api.keen.io/3.0/projects/5337e28273f4bb4499000000/events/click?api_key=a0bb828de21e953a675610cb6e6b8935537b19c2f0ac33937d6d1df2cc8fddbf379a81ad398618897b70d15c6b42647c3e063a689bc367f5c32b66c18010541c0ad1cf3dbd36100dc4475c306b238cb6f5b05f082dc4071e35094a722b1f3e29fad63c933ea8e33e8b892c770df5e1bb&data=eyJwYWdlIjogIkRhdGEgTW9kZWxpbmcgR3VpZGUiLCJyZWZlcnJlciI6ICJSRUFETUUubWQifQ==&redirect=https://github.com/keen/data-modeling-guide/)
[API Technical Reference](https://api.keen.io/3.0/projects/5337e28273f4bb4499000000/events/click?api_key=a0bb828de21e953a675610cb6e6b8935537b19c2f0ac33937d6d1df2cc8fddbf379a81ad398618897b70d15c6b42647c3e063a689bc367f5c32b66c18010541c0ad1cf3dbd36100dc4475c306b238cb6f5b05f082dc4071e35094a722b1f3e29fad63c933ea8e33e8b892c770df5e1bb&data=eyJwYWdlIjogIkFQSSBUZWNobmljYWwgUmVmZXJlbmNlIiwicmVmZXJyZXIiOiAiUkVBRE1FLm1kIn0=&redirect=https://keen.io/docs/api/reference/?s=gh_js)
diff --git a/bower.json b/bower.json
index 6b7b86a..71cc8a3 100644
--- a/bower.json
+++ b/bower.json
@@ -1,7 +1,8 @@
{
"name": "keen-js",
- "version": "3.1.0",
- "homepage": "https://github.com/keen/keen-js",
+ "description": "Keen IO JavaScript SDK",
+ "main": "./dist/keen.min.js",
+ "license": "MIT",
"authors": [
"Dustin Larimer ",
"Sean Dokko ",
@@ -14,7 +15,6 @@
"Micah Wolfe ",
"Tim Schiller "
],
- "description": "JS Library for the Keen IO API",
"keywords": [
"analytics",
"metrics",
@@ -26,24 +26,28 @@
"keen.io",
"keen-io"
],
- "license": "MIT",
- "main": "./dist/keen.min.js",
+ "homepage": "https://github.com/keen/keen-js",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/keen/keen-js.git"
+ },
"ignore": [
"**/.*",
- "config",
"bower_components",
"node_modules",
+ "docs",
"src",
"test",
- "Gruntfile.js",
+ "gulpfile.js",
+ "karma.conf.js",
+ ".npmignore",
"*.json",
- "*.md"
+ "*.md",
+ "*.yml"
],
"devDependencies": {
"mocha": "~1.18.2",
- "chai": "~1.9.1",
- "sinon": "http://sinonjs.org/releases/sinon-1.9.0.js",
- "sinon-ie": "http://sinonjs.org/releases/sinon-ie-1.9.0.js",
- "requirejs-bower": "2.1.14"
+ "requirejs-bower": "2.1.14",
+ "sinon-1.12.1": "http://sinonjs.org/releases/sinon-1.12.1.js"
}
}
diff --git a/config/aws.js b/config/aws.js
deleted file mode 100644
index aafdfea..0000000
--- a/config/aws.js
+++ /dev/null
@@ -1,14 +0,0 @@
-module.exports = function(){
-
- if (typeof process.env.AWS_KEY !== "undefined") {
- this.key = process.env.AWS_KEY;
- }
-
- if (typeof process.env.AWS_SECRET !== "undefined") {
- this.secret = process.env.AWS_SECRET;
- }
-
- this.bucket = 'keen-js';
-
- return this;
-};
diff --git a/config/sauce_connect_setup.sh b/config/sauce_connect_setup.sh
deleted file mode 100644
index dbc5ba7..0000000
--- a/config/sauce_connect_setup.sh
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/bin/bash
-
-set -e
-
-# Setup and start Sauce Connect for your TravisCI build
-# This script requires your .travis.yml to include the following two private env variables:
-# SAUCE_USERNAME
-# SAUCE_ACCESS_KEY
-# Follow the steps at https://saucelabs.com/opensource/travis to set that up.
-#
-# Curl and run this script as part of your .travis.yml before_script section:
-# before_script:
-# - curl https://gist.github.com/santiycr/5139565/raw/sauce_connect_setup.sh -L | bash
-
-CONNECT_URL="http://saucelabs.com/downloads/Sauce-Connect-latest.zip"
-CONNECT_DIR="/tmp/sauce-connect-$RANDOM"
-CONNECT_DOWNLOAD="Sauce_Connect.zip"
-
-CONNECT_LOG="$LOGS_DIR/sauce-connect"
-CONNECT_STDOUT="$LOGS_DIR/sauce-connect.stdout"
-CONNECT_STDERR="$LOGS_DIR/sauce-connect.stderr"
-
-READY_FILE="connect-ready-$RANDOM"
-
-# Get Connect and start it
-mkdir -p $CONNECT_DIR
-cd $CONNECT_DIR
-curl $CONNECT_URL -o $CONNECT_DOWNLOAD 2> /dev/null 1> /dev/null
-unzip $CONNECT_DOWNLOAD > /dev/null
-rm $CONNECT_DOWNLOAD
-
-echo "Starting Sauce Connect in the background, logging into:"
-echo " $CONNECT_LOG"
-echo " $CONNECT_STDOUT"
-echo " $CONNECT_STDERR"
-
-java -jar Sauce-Connect.jar --readyfile $READY_FILE \
- --tunnel-identifier $TRAVIS_JOB_NUMBER \
- $SAUCE_USERNAME $SAUCE_ACCESS_KEY &
-
-# Wait for Connect to be ready before exiting
-while [ ! -f $READY_FILE ]; do
- sleep .5
-done
diff --git a/config/saucelabs-ie.js b/config/saucelabs-ie.js
deleted file mode 100644
index 56552a9..0000000
--- a/config/saucelabs-ie.js
+++ /dev/null
@@ -1,37 +0,0 @@
-module.exports = function(){
-
- this.username = 'keenlabs-js';
- this.urls = [
- 'http://127.0.0.1:9999/ie-legacy/',
- 'http://127.0.0.1:9999/ie-legacy/'
- ];
-
- if (typeof process.env.SAUCE_ACCESS_KEY !== "undefined") {
- this.key = process.env.SAUCE_ACCESS_KEY;
- }
-
- this.browsers = [
- {
- browserName: "internet explorer",
- platform: "WINDOWS 7",
- version: "8"
- },
- {
- browserName: "internet explorer",
- platform: "WINDOWS XP",
- version: "8"
- },
- {
- browserName: "internet explorer",
- platform: "WINDOWS XP",
- version: "7"
- },
- {
- browserName: "internet explorer",
- platform: "WINDOWS XP",
- version: "6"
- }
- ];
-
- return this;
-};
diff --git a/config/saucelabs.js b/config/saucelabs.js
deleted file mode 100644
index ea4532a..0000000
--- a/config/saucelabs.js
+++ /dev/null
@@ -1,296 +0,0 @@
-module.exports = function(){
-
- this.username = 'keenlabs-js';
-
- this.key = process.env.SAUCE_ACCESS_KEY;
- this.buildID = process.env.TRAVIS_JOB_ID;
-
- this.urls = ['http://127.0.0.1:9999/index.html'];
- this.concurrency = 2;
- this.maxRetries = 3;
- this.maxDuration = 240;
-
- this.additionalConfig = {
- 'record-video': false,
- 'video-upload-on-pass': false,
- 'record-screenshots': false
- };
-
- this.browsers = [
-
- /* FIREFOX */
- {
- browserName: "firefox",
- platform: "OS X 10.9",
- version: "beta"
- },
- {
- browserName: 'firefox',
- platform: 'Windows 8.1',
- version: "beta"
- },
- {
- browserName: 'firefox',
- platform: 'Windows 8',
- version: "beta"
- },
-
- {
- browserName: "firefox",
- platform: "OS X 10.9"
- },
- {
- browserName: "firefox",
- platform: "OS X 10.6"
- },
- {
- browserName: "firefox",
- platform: "Linux"
- },
- {
- browserName: 'firefox',
- platform: 'Windows 8.1'
- },
- {
- browserName: 'firefox',
- platform: 'Windows 8'
- },
- {
- browserName: "firefox",
- platform: "Windows 7"
- },
- {
- browserName: "firefox",
- platform: "Windows XP"
- },
-
-
-
- /* CHROME */
-
- {
- browserName: "chrome",
- platform: "OS X 10.8",
- version: "beta"
- },
- {
- browserName: "chrome",
- platform: "Windows 8.1",
- version: "beta"
- },
- {
- browserName: "chrome",
- platform: "Windows 8",
- version: "beta"
- },
-
-
- {
- browserName: "chrome",
- platform: "OS X 10.9"
- },
- {
- browserName: "chrome",
- platform: "OS X 10.8"
- },
- {
- browserName: "chrome",
- platform: "OS X 10.6"
- },
- {
- browserName: 'chrome',
- platform: 'Windows 8.1'
- },
- {
- browserName: 'chrome',
- platform: 'Windows 8'
- },
- {
- browserName: "chrome",
- platform: "Windows 7"
- },
- {
- browserName: "chrome",
- platform: "Windows XP"
- },
- {
- browserName: "chrome",
- platform: "Linux"
- },
-
-
- /* SAFARI */
- {
- browserName: "safari",
- platform: "OS X 10.9",
- version: "7"
- },
- {
- browserName: "safari",
- platform: "OS X 10.8",
- version: "6"
- },
- {
- browserName: "safari",
- platform: "OS X 10.6",
- version: "5"
- },
-
-
-
- /* Internet Explorer */
-
- {
- browserName: "internet explorer",
- platform: "Windows 8.1",
- version: "11"
- },
-
- {
- browserName: "internet explorer",
- platform: "Windows 8",
- version: "10"
- },
-
-
- {
- browserName: "internet explorer",
- platform: "WINDOWS 7",
- version: "11"
- },
- {
- browserName: 'internet explorer',
- platform: 'Windows 7',
- version: '10'
- },
- {
- browserName: "internet explorer",
- platform: "WINDOWS 7",
- version: "9"
- },
-
- // Testing framework not supported :\
-
- // {
- // browserName: "internet explorer",
- // platform: "WINDOWS 7",
- // version: "8"
- // }
-
- // {
- // browserName: "internet explorer",
- // platform: "Windows XP",
- // version: "8"
- // },
- // {
- // browserName: "internet explorer",
- // platform: "Windows XP",
- // version: "7"
- // },
- // {
- // browserName: "internet explorer",
- // platform: "Windows XP",
- // version: "6"
- // }
-
- /* iOS */
-
- {
- browserName: "iphone",
- platform: "OS X 10.9",
- version: "7.1",
- deviceName: "iPhone"
- },
- // {
- // browserName: "iphone",
- // platform: "OS X 10.9",
- // version: "7.0",
- // deviceName: "iPhone"
- // },
- {
- browserName: "iphone",
- platform: "OS X 10.8",
- version: "6.1",
- deviceName: "iPhone"
- },
- // {
- // browserName: "iphone",
- // platform: "OS X 10.8",
- // version: "6.0",
- // deviceName: "iPhone"
- // },
-
-
- /* Android */
-
- {
- browserName: "android",
- platform: "Linux",
- version: "4.4"
- },
- // {
- // browserName: "android",
- // platform: "Linux",
- // version: "4.4",
- // deviceName: "Google Nexus 7 HD Emulator"
- // },
- // {
- // browserName: "android",
- // platform: "Linux",
- // version: "4.4",
- // deviceName: "LG Nexus 4 Emulator"
- // },
-
- // {
- // browserName: "android",
- // platform: "Linux",
- // version: "4.3"
- // },
-
- // {
- // browserName: "android",
- // platform: "Linux",
- // version: "4.2"
- // },
- // {
- // browserName: "android",
- // platform: "Linux",
- // version: "4.2",
- // deviceName: "Samsung Galaxy Tab 3 Emulator"
- // },
-
- {
- browserName: "android",
- platform: "Linux",
- version: "4.1"
- },
- // {
- // browserName: "android",
- // platform: "Linux",
- // version: "4.1",
- // deviceName: "Samsung Galaxy Note Emulator"
- // },
- //
- // {
- // browserName: "android",
- // platform: "Linux",
- // version: "4.0",
- // deviceName: "Motorola Droid 4 Emulator"
- // },
- // {
- // browserName: "android",
- // platform: "Linux",
- // version: "4.0",
- // deviceName: "Samsung Galaxy Nexus Emulator"
- // },
- // {
- // browserName: "android",
- // platform: "Linux",
- // version: "4.0",
- // deviceName: "Samsung Galaxy Note Emulator"
- // }
-
- ];
-
- return this;
-};
diff --git a/config/wrappers.js b/config/wrappers.js
deleted file mode 100644
index d613cce..0000000
--- a/config/wrappers.js
+++ /dev/null
@@ -1,41 +0,0 @@
-module.exports = function(){
- return {
-
- libraryBanner: '!function(name, context, definition){' +
- 'if (typeof define == "function" && define.amd) {' +
- 'define("keen", [], function(lib){ return definition(); });' +
- '}' +
- 'if ( typeof module === "object" && typeof module.exports === "object" ) {' +
- 'module.exports = definition();' +
- '} else {' +
- 'context[name] = definition();' +
- '}' +
- '}("Keen", this, function(Keen) {' +
- '"use strict";\n\n',
-
- libraryFooter: 'if (Keen.loaded) {' +
- 'setTimeout(function(){' +
- 'Keen.utils.domready(function(){' +
- 'Keen.trigger("ready");' +
- '});' +
- '}, 0);' +
- '}' +
- '_loadAsync();' +
- '\n\nreturn Keen; \n\n});',
-
- adapterBanner: '!function(name, context, definition){' +
- 'if (typeof define == "function" && define.amd) {' +
- 'define(["keen"], function(lib){ definition(lib); });' +
- '}' +
- 'if ( typeof module === "object" && typeof module.exports === "object" ) {' +
- 'module.exports = definition();' +
- '} else {' +
- 'definition(context[name]);' +
- '}' +
- '}("Keen", this, function(Keen) {' +
- '"use strict";\n\n',
-
- adapterFooter: '\n\nreturn Keen; \n\n});'
-
- }
-};
diff --git a/docs/README.md b/docs/README.md
index 62b249b..e1e4869 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -2,21 +2,20 @@
If you haven’t done so already, [login to Keen IO to create a project](https://keen.io/add-project) for your app. The Project ID and API Keys are available on the Project Overview page. You will need these for the next steps.
-
## Install the library
Load this library asynchronously from our CDN by copy/pasting this snippet of JavaScript above the `` tag of your page.
```html
```
Alternatively, you can load the library synchronously from our CDN:
```html
-
+
```
Read our [Installation guide](./installation.md) to learn about all the ways this library can fit into your workflow.
diff --git a/docs/dataset.md b/docs/dataset.md
index 7bf476a..2b06f3a 100644
--- a/docs/dataset.md
+++ b/docs/dataset.md
@@ -1,5 +1,25 @@
# Keen.Dataset
+Every instance of `Keen.Dataviz` manages its own internal instance of `Keen.Dataset`. These methods can be called directly for advanced manipulation of data at any point in the visualization process.
+
+```javascript
+var chart = new Keen.Dataset();
+
+// Parses raw data into a new internal Dataset instance
+chart
+ .parseRawData({ result: 2450 });
+
+chart
+ .call(function(){
+ // Modify internal Dataset instance
+ this.dataset.updateColumn(1, function(value, index, column){
+ return value / 100;
+ });
+ })
+ .render();
+
+```
+
## Create a new Dataset instance
```javascript
diff --git a/docs/dataviz.md b/docs/dataviz.md
index b832d92..e3bbab9 100644
--- a/docs/dataviz.md
+++ b/docs/dataviz.md
@@ -17,12 +17,18 @@ chart
.sortGroups("desc")
.prepare();
-client.run(query, function(){
+var req = client.run(query, function(){
chart
.parseRequest(this)
.render();
});
+// let's update this chart every 10 seconds
+setInterval(function(){
+ chart.prepare(); // restart the spinner
+ req.refresh();
+}, 10*1000);
+
```
## DOM Element
diff --git a/docs/installation.md b/docs/installation.md
index 51d7e07..975b9c2 100644
--- a/docs/installation.md
+++ b/docs/installation.md
@@ -1,16 +1,23 @@
# Installation
-## Asynchronous Loading
-Copy+paste this snippet of JavaScript above the `` tag of your page.
+```ssh
+# via npm
+$ npm install keen-js
+
+# or bower
+$ bower install keen-js
+```
+
+For quick browser use, copy/paste this snippet of JavaScript above the `` tag of your page:
```html
```
-`addEvent`, `setGlobalProperties`, `trackExternalLink` and `ready` methods are available immediately, but data will be cached locally. Once the library has loaded, any methods you have called will be "replayed" and cached data will be sent to our service.
+`addEvent`, `setGlobalProperties`, and `trackExternalLink` methods are available immediately, but data will be cached locally. Once the library has loaded, any methods you have called will be "replayed" and cached data will be sent to our service.
```javascript
// paste async code snippet here
@@ -23,7 +30,7 @@ client.addEvent("pageview", { key: "value" });
### Keen.ready(fn)
-Unlike the core `Keen` object and its tracking-related methods, `Keen.Query`, `Keen.Visualization`, `Keen.Dataviz` and `Keen.Dataset` won't be available until the library has finished loading. Use our ready callback, `Keen.ready()`, to safely wrap any references to these objects.
+Unlike the core `Keen` object and its tracking-related methods, `Keen.Query`, `Keen.Visualization`, `Keen.Dataviz` and `Keen.Dataset` won't be available until the library has finished loading. Use our ready callback, `Keen.ready()`, to safely wrap any references to these objects.
```javascript
Keen.ready(function(){
@@ -42,25 +49,17 @@ Keen.ready(function(){
Simple, synchronous loading.
-**Specified version:** (recommended)
-
-```html
-
-```
-
-**Latest build:**
-
```html
-
+
```
-## Bower
-
-If you're using [Bower](http://bower.io/), you can load this library into your project with the following command:
+## Tracking-only
-`$ bower install keen-js`
+If you only need to track events, replace the URLs in your installation with this version:
-Full and tracking-only versions of the library will be retrieved.
+```
+https://d26b395fwzu5fz.cloudfront.net/3.2.0/keen-tracker.min.js
+```
## AMD/CommonJS
@@ -73,51 +72,10 @@ The library is loaded with an explicitly named module ID ("keen"), which present
```javascript
requirejs.config({
"paths": {
- "keen": "https://d26b395fwzu5fz.cloudfront.net/latest/keen.js"
+ "keen": "https://d26b395fwzu5fz.cloudfront.net/3.2.0/keen.js"
}
});
require([ "keen" ], function(Keen) {
var client = new Keen({ ... });
});
```
-
-## Tracking-only
-
-If you only need to track events, replace the URLs in your installation with this version:
-
-```
-https://d26b395fwzu5fz.cloudfront.net/3.1.0/keen-tracker.min.js
-```
-
-or for the latest version:
-
-```
-https://d26b395fwzu5fz.cloudfront.net/latest/keen-tracker.min.js
-```
-
-For production implementations, we highly recommend sticking with a specific version of the library, rather than `latest/`, to ensure future changes won't cause unforeseen issues.
-
-
-## Custom Builds
-
-Build your own custom versions of this library with [Grunt](http://gruntjs.com/getting-started) (requires Node.js/npm).
-
-```bash
-$ git clone https://github.com/keenlabs/keen-js.git && cd keen-js
-$ npm install
-$ bower install
-$ grunt build
-```
-
-Built files will be placed in the `dist` directory.
-
-
-### Test Coverage
-
-Fire up the test server
-
-```bash
-$ grunt dev
-```
-
-Results are available at `http://localhost:9999`
diff --git a/docs/meta.md b/docs/meta.md
new file mode 100644
index 0000000..eb030b0
--- /dev/null
+++ b/docs/meta.md
@@ -0,0 +1,125 @@
+# Access Meta Data
+
+## All collections
+
+Retrieve schemas for all collections in a project
+
+```javascript
+var client = new Keen({
+ projectId: "YOUR_PROJECT_ID",
+ masterKey: "YOUR_MASTER_KEY"
+});
+
+var url = client.url("/events");
+// returns https://api.keen.io/3.0/projects/YOUR_PROJECT_ID/events
+
+client.get(url, null, client.masterKey(), function(err, res){
+ // if (err) handle the error
+ console.log("Returned schema info for " + res.length + " collections");
+});
+```
+
+Each collection will return a result that looks like this:
+
+```json
+{
+ "name": "pageview",
+ "properties": {
+ "keen.created_at": "datetime",
+ "keen.id": "string",
+ "keen.timestamp": "datetime",
+ "another_property": "string",
+ "etc": "string"
+ },
+ "url": "/3.0/projects//events/"
+}
+```
+
+
+## Single collection
+
+```javascript
+var client = new Keen({
+ projectId: "YOUR_PROJECT_ID",
+ masterKey: "YOUR_MASTER_KEY"
+});
+var url = client.get("/events/purchases");
+// returns https://api.keen.io/3.0/projects/YOUR_PROJECT_ID/events/purchases
+
+client.get(url, null, client.masterKey(), function(err, res){
+ // if (err) handle the error
+ console.log(res);
+});
+```
+
+The response will look like this:
+
+```json
+{
+ "properties": {
+ "keen.created_at": "datetime",
+ "keen.id": "string",
+ "keen.timestamp": "datetime",
+ "another_property": "string",
+ "etc": "string"
+ }
+
+}
+```
+
+
+## All properties from a collection
+
+```javascript
+var client = new Keen({
+ projectId: "YOUR_PROJECT_ID",
+ masterKey: "YOUR_MASTER_KEY"
+});
+var url = client.get("/events/purchases/properties");
+// returns https://api.keen.io/3.0/projects/YOUR_PROJECT_ID/events/purchases/properties
+
+client.get(url, null, client.masterKey(), function(err, res){
+ // if (err) handle the error
+ console.log("Returned property info for " + res.length + " properties");
+});
+```
+
+The response will include an array of objects that look like this:
+
+```json
+[
+ {
+ "property_name": "keen.id",
+ "type": "string",
+ "url": "/3.0/projects/YOUR_PROJECT_ID/events/purchases/properties/keen.id"
+ },
+ {},
+ {}
+]
+```
+
+## Single property from a collection
+
+```javascript
+var client = new Keen({
+ projectId: "YOUR_PROJECT_ID",
+ masterKey: "YOUR_MASTER_KEY"
+});
+var url = client.get("/events/purchases/properties/keen.id");
+// returns https://api.keen.io/3.0/projects/YOUR_PROJECT_ID/events/purchases/properties
+
+client.get(url, null, client.masterKey(), function(err, res){
+ // if (err) handle the error
+ console.log(res);
+});
+```
+
+The response will look like this (same as a single result from the previous example):
+
+```json
+{
+ "property_name": "keen.id",
+ "type": "string",
+ "url": "/3.0/projects/YOUR_PROJECT_ID/events/purchases/properties/keen.id"
+}
+```
diff --git a/docs/query.md b/docs/query.md
index 1a348de..5d24ebd 100644
--- a/docs/query.md
+++ b/docs/query.md
@@ -42,7 +42,7 @@ var your_analysis = new Keen.Query(analysisType, params);
});
// Send query
- client.run(count, function(response){
+ client.run(count, function(err, response){
// Print the query result to the console
console.log(response);
});
@@ -106,7 +106,7 @@ var max_revenue = new Keen.Query("maximum", {
groupBy: "geo.country"
});
-var mashup = client.run([avg_revenue, max_revenue], function(res){
+var mashup = client.run([avg_revenue, max_revenue], function(err, res){
// res[0].result or this.data[0] (avg_revenue)
// res[1].result or this.data[1] (max_revenue)
});
diff --git a/docs/recipes.md b/docs/recipes.md
index 741252b..bf937c1 100644
--- a/docs/recipes.md
+++ b/docs/recipes.md
@@ -27,7 +27,19 @@ var uniqueVisitors = new Keen.Query("count_unique", { // second query
timeframe: timeframe
});
-client.run([pageviews, uniqueVisitors], function(response){ // run the queries
+var chart = new Keen.Dataviz()
+ .el(document.getElementById("pageviews"))
+ .chartType("linechart")
+ .chartOptions({
+ hAxis: {
+ format:'MMM d',
+ gridlines: {count: 12}
+ }
+ })
+ .prepare();
+
+client.run([pageviews, uniqueVisitors], function(err, response){ // run the queries
+ // if (err) handle the error
var result1 = response[0].result // data from first query
var result2 = response[1].result // data from second query
@@ -43,17 +55,11 @@ client.run([pageviews, uniqueVisitors], function(response){ // run the queries
{ category: "Visitors", result: result2[i]["value"] }
]
}
- if (i == result1.length-1) { // chart the data
- window.chart = new Keen.Visualization({result: data}, document.getElementById('pageviews'), {
- chartType: "linechart",
- title: " ",
- chartOptions: {
- hAxis: {
- format:'MMM d',
- gridlines: {count: 12}
- }
- }
- });
+ if (i == result1.length-1) {
+ // chart the data
+ chart
+ .parseRawData({ result: data })
+ .render();
}
i++;
}
@@ -91,7 +97,19 @@ Keen.ready(function(){
timeframe: timeframe
});
- client.run([posts, uniquePosters], function(response){ // run the queries
+ var chart = new Keen.Dataviz()
+ .el(document.getElementById("chart1"))
+ .chartType("linechart")
+ .chartOptions({
+ lineWidth: 3,
+ hAxis: {
+ format:'MMM d',
+ gridlines: {count: 12}
+ }
+ })
+ .prepare();
+
+ client.run([posts, uniquePosters], function(err, response){ // run the queries
var result1 = response[0].result // data from first query
var result2 = response[1].result // data from second query
@@ -106,18 +124,11 @@ Keen.ready(function(){
{ category: "Posts per peep", result: result1[i]["value"] / result2[i]["value"]}
]
}
- if (i == result1.length-1) { // chart the data
- window.chart = new Keen.Visualization({result: data}, document.getElementById('chart1'), {
- chartType: "linechart",
- title: " ",
- chartOptions: {
- lineWidth: 3,
- hAxis: {
- format:'MMM d',
- gridlines: {count: 12}
- }
- }
- });
+ if (i == result1.length-1) {
+ // chart the data
+ chart
+ .parseRawData({ result: data })
+ .render();
}
i++;
}
@@ -142,7 +153,7 @@ Each data point on the line chart tells you what percentage of users who signed
-
+
-
+
+
-
-
-
diff --git a/test/ie-legacy/index.html b/test/ie-legacy/index.html
deleted file mode 100644
index de4e736..0000000
--- a/test/ie-legacy/index.html
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
- Keen IO JS | Synchronous IE Legacy Test
-
-
-
-
-
-
-
-
-
diff --git a/test/index.html b/test/index.html
deleted file mode 100644
index 006a68f..0000000
--- a/test/index.html
+++ /dev/null
@@ -1,71 +0,0 @@
-
-
-
-
- Keen IO JS SDK Test
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/test/keen-helper.js b/test/keen-helper.js
deleted file mode 100644
index 5936e4e..0000000
--- a/test/keen-helper.js
+++ /dev/null
@@ -1,16 +0,0 @@
-var expect = chai.expect;
-var keenHelper = {
- projectId: '5235435645bf82348762',
- writeKey: '43232342f234g234234',
- readKey: '42352gkjhgj1g2424',
- protocol: 'https',
- host: 'mocha.keen.io/9999/3.0',
- //host: 'api.keen.io/9999/3.0',
- collection: 'mocha',
- properties: { username: 'keenio', color: 'blue' },
- responses: {
- success: '{\"created\": true }',
- error: '{\"error\": true }'
- }
-};
-Keen.debug = true;
diff --git a/test/unit/core.js b/test/unit/core.js
deleted file mode 100644
index c35683f..0000000
--- a/test/unit/core.js
+++ /dev/null
@@ -1,160 +0,0 @@
-describe("Keen (core)", function() {
-
- beforeEach(function() {
- this.project = new Keen({
- projectId: keenHelper.projectId,
- readKey: keenHelper.readKey,
- writeKey: keenHelper.writeKey
- });
- });
-
- describe("constructor", function(){
- it("should create a new Keen instance", function(){
- expect(this.project).to.be.an.instanceof(Keen);
- });
- it("should create a new config object", function(){
- expect(this.project.config).to.be.ok;
- });
- it("should create a new config object when config argument is omitted", function(){
- var empty = new Keen();
- expect(empty.config).to.be.ok;
- });
- });
-
- describe("projectId", function(){
-
- it("should set the projectId (string)", function() {
-
- expect(this.project.config)
- .to.have.property('projectId')
- .that.is.a('string')
- .that.equals(keenHelper.projectId);
-
- });
-
- });
-
- describe("readKey", function(){
-
- it("should set the readKey (string)", function() {
- expect(this.project.config)
- .to.have.property('readKey')
- .that.is.a('string')
- .that.equals(keenHelper.readKey);
-
- });
-
- });
-
- describe("writeKey", function(){
-
- it("should set the writeKey (string)", function() {
- expect(this.project.config)
- .to.have.property('writeKey')
- .that.is.a('string')
- .that.equals(keenHelper.writeKey);
-
- });
-
- });
-
- describe("protocol", function(){
-
- it("should default to \"https\" if protocol is absent", function(){
-
- // Empty
- var keen_empty = new Keen({ projectId: '123' });
- expect(keen_empty.config.protocol).to.equal('https');
-
- });
-
- it("should set protocol to \"https\" if designated", function(){
-
- var keen = new Keen({ projectId: '123', protocol: 'https' });
- expect(keen.config.protocol).to.equal('https');
-
- });
-
- it("should set protocol to \"http\" if designated", function(){
-
- var keen = new Keen({ projectId: '123', protocol: 'http' });
- expect(keen.config.protocol).to.equal('http');
-
- });
-
- });
-
- describe("requestType", function(){
-
- it("should set request type to \"jsonp\" by default", function(){
-
- var keen = new Keen({ projectId: '123' });
- expect(keen.config)
- .to.have.property('requestType')
- .that.is.a('string')
- .that.equals('jsonp');
-
- /*if ('withCredentials' in new XMLHttpRequest()) {
- expect(keen.config)
- .to.have.property('requestType')
- .that.is.a('string')
- .that.equals('xhr');
- } else {
- expect(keen.config)
- .to.have.property('requestType')
- .that.is.a('string')
- .that.equals('jsonp');
- }*/
-
- });
-
- it("should set request type to \"xhr\" if designated and CORS supported, otherwise fall back \"JSONP\"", function(){
-
- var keen = new Keen({ projectId: '123', requestType: 'xhr' });
- if ('withCredentials' in new XMLHttpRequest()) {
- expect(keen.config)
- .to.have.property('requestType')
- .that.is.a('string')
- .that.equals('xhr');
- } else {
- expect(keen.config)
- .to.have.property('requestType')
- .that.is.a('string')
- .that.equals('jsonp');
- }
-
- });
-
- it("should set request type to \"jsonp\" if designated", function(){
-
- var keen = new Keen({ projectId: '123', requestType: 'jsonp' });
- expect(keen.config)
- .to.have.property('requestType')
- .that.is.a('string')
- .that.equals('jsonp');
-
- });
-
- it("should set request type to \"beacon\" if designated", function(){
-
- var keen = new Keen({ projectId: '123', requestType: 'beacon' });
- expect(keen.config)
- .to.have.property('requestType')
- .that.is.a('string')
- .that.equals('beacon');
-
- });
-
- });
-
- describe("Keen.urlMaxLength threshold", function(){
- it("should be 2000 for IE, and 16000 otherwise", function(){
- if (navigator.userAgent.indexOf('MSIE') !== -1 || navigator.appVersion.indexOf('Trident/') > 0) {
- expect(Keen.urlMaxLength).to.eql(2000);
- } else {
- expect(Keen.urlMaxLength).to.eql(16000);
- }
- });
- });
-
-});
diff --git a/test/unit/helpers/mockServerRequests.js b/test/unit/helpers/mockServerRequests.js
new file mode 100644
index 0000000..70d8e8b
--- /dev/null
+++ b/test/unit/helpers/mockServerRequests.js
@@ -0,0 +1,32 @@
+var nock = require('nock'),
+ keenHelper = require("./test-config");
+
+var baseUrl = "https://api.keen.io/3.0",
+ resHeader = { "Content-Type": "application/json" };
+
+module.exports = {
+ "post": mockPostRequest,
+ "get": mockGetRequest,
+ "del": mockDelRequest
+};
+
+function mockPostRequest(path, responseCode, responseBody, delay){
+ nock(baseUrl)
+ .post("/3.0/projects/" + keenHelper.projectId + path)
+ .delay(delay || 0)
+ .reply(responseCode, responseBody, resHeader);
+}
+
+function mockGetRequest(path, responseCode, responseBody, delay){
+ nock(baseUrl)
+ .get("/3.0/projects/" + keenHelper.projectId + path)
+ .delay(delay || 0)
+ .reply(responseCode, responseBody, resHeader);
+}
+
+function mockDelRequest(path, responseCode, responseBody, delay) {
+ nock(baseUrl)
+ .delete("/3.0/projects/" + keenHelper.projectId + path)
+ .delay(delay || 0)
+ .reply(responseCode, responseBody, resHeader);
+}
diff --git a/test/unit/helpers/test-config.js b/test/unit/helpers/test-config.js
new file mode 100644
index 0000000..3dd543b
--- /dev/null
+++ b/test/unit/helpers/test-config.js
@@ -0,0 +1,17 @@
+module.exports = {
+ projectId : "52f00ec205cd66404b000000",
+ writeKey : "554a723d023da6cb24e51c56a9a54555e9dcf8403d4b71ffa37e9112295622e78a10eed43a13c83b14ce171b0f1317bb09aa8df43d50f73b77709ab431af611ea47ed65f4d74c0ea5f2bde8407322ab70559afef294673ee6c224308b1744c9e069508799edefc51264b3f75a1ba9e26",
+ readKey : "f979d53e026bdbf1ba16f01ce168bc7bcf9d5308ba672fb14e1834793a9e705eefa04793f0f87fb76b1d49a6cc2747b96a8abae0e4569d70314099b3f7790f55e98c9ac482e3883aab86a4bb577c295dbbca4867e95e2e4b15038fac5d80957ded3e868e4e6e319d3aa9275abc22b16e",
+ masterKey : "FC135394DD08E3976870B7E7E83BDCD8",
+ protocol : "https",
+ host : "mocha.keen.io/9999/3.0",
+ collection : "mocha",
+ properties: {
+ username : "keenio",
+ color : "blue"
+ },
+ responses: {
+ success : '{\"created\": true }',
+ error : '{\"error\": true }'
+ }
+};
diff --git a/test/unit/index.html b/test/unit/index.html
new file mode 100644
index 0000000..b3deda0
--- /dev/null
+++ b/test/unit/index.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+ Keen IO JavaScript SDK Unit Tests
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/unit/index.js b/test/unit/index.js
new file mode 100644
index 0000000..d648740
--- /dev/null
+++ b/test/unit/index.js
@@ -0,0 +1,14 @@
+// -------------------------
+// Common modules
+// -------------------------
+require("./modules/core/client-spec");
+require("./modules/core/events-spec");
+require("./modules/core/query-spec");
+require("./modules/dataset/dataset-spec");
+
+// -------------------------
+// Browser-specific modules
+// -------------------------
+require("./modules/core/tracker/browser-spec");
+require("./modules/core/request/browser-spec");
+require("./modules/dataviz/dataviz-spec");
diff --git a/test/unit/modules/core/client-spec.js b/test/unit/modules/core/client-spec.js
new file mode 100644
index 0000000..a231c6e
--- /dev/null
+++ b/test/unit/modules/core/client-spec.js
@@ -0,0 +1,129 @@
+var expect = require("chai").expect;
+
+var Keen = require("../../../../src/core"),
+ keenHelper = require("../../helpers/test-config");
+
+describe("Client", function() {
+
+ beforeEach(function() {
+ this.project = new Keen({
+ projectId : keenHelper.projectId,
+ readKey : keenHelper.readKey,
+ writeKey : keenHelper.writeKey,
+ masterKey : keenHelper.masterKey
+ });
+ });
+
+ describe("constructor", function(){
+ it("should create a new Keen instance", function(){
+ expect(this.project).to.be.an.instanceof(Keen);
+ });
+ it("should create a new config object", function(){
+ expect(this.project.config).to.be.ok;
+ });
+ it("should create a new config object when config argument is omitted", function(){
+ var empty = new Keen();
+ expect(empty.config).to.be.ok;
+ });
+ });
+
+ describe("#configure", function(){
+
+ describe("protocol", function(){
+ it("should default to \"https\" if protocol is absent", function(){
+ var keen_empty = new Keen({ projectId: '123' });
+ expect(keen_empty.config.protocol).to.equal('https');
+ });
+ it("should set protocol to \"https\" if designated", function(){
+ var keen = new Keen({ projectId: '123', protocol: 'https' });
+ expect(keen.config.protocol).to.equal('https');
+ });
+ it("should set protocol to \"http\" if designated", function(){
+ var keen = new Keen({ projectId: '123', protocol: 'http' });
+ expect(keen.config.protocol).to.equal('http');
+ });
+ });
+
+ describe("requestType", function(){
+ it("should set request type to \"jsonp\" by default", function(){
+ var keen = new Keen({ projectId: '123' });
+ expect(keen.config)
+ .to.have.property('requestType')
+ .that.is.a('string')
+ .that.equals('jsonp');
+ });
+ it("should set request type to \"jsonp\" if designated", function(){
+ var keen = new Keen({ projectId: '123', requestType: 'jsonp' });
+ expect(keen.config)
+ .to.have.property('requestType')
+ .that.is.a('string')
+ .that.equals('jsonp');
+ });
+ it("should set request type to \"beacon\" if designated", function(){
+ var keen = new Keen({ projectId: '123', requestType: 'beacon' });
+ expect(keen.config)
+ .to.have.property('requestType')
+ .that.is.a('string')
+ .that.equals('beacon');
+ });
+ });
+
+ });
+
+ describe("#projectId", function(){
+ it("should set the projectId (string)", function() {
+ expect(this.project.config)
+ .to.have.property('projectId')
+ .that.is.a('string')
+ .that.equals(keenHelper.projectId);
+ });
+ it("should remove the projectId by passing null", function(){
+ var client = new Keen({ projectId: "123" });
+ client.projectId(null);
+ expect(client.config.projectId).to.not.exist;
+ });
+ });
+
+ describe("#readKey", function(){
+ it("should set the readKey (string)", function() {
+ expect(this.project.config)
+ .to.have.property('readKey')
+ .that.is.a('string')
+ .that.equals(keenHelper.readKey);
+ });
+ it("should remove the readKey by passing null", function(){
+ var client = new Keen({ readKey: "123" });
+ client.readKey(null);
+ expect(client.config.readKey).to.not.exist;
+ });
+ });
+
+ describe("#writeKey", function(){
+ it("should set the writeKey (string)", function() {
+ expect(this.project.config)
+ .to.have.property('writeKey')
+ .that.is.a('string')
+ .that.equals(keenHelper.writeKey);
+ });
+ it("should remove the writeKey by passing null", function(){
+ var client = new Keen({ writeKey: "123" });
+ client.writeKey(null);
+ expect(client.config.writeKey).to.not.exist;
+ });
+ });
+
+ describe("#masterKey", function(){
+ it("should set the masterKey (string)", function() {
+ expect(this.project.config)
+ .to.have.property('masterKey')
+ .that.is.a('string')
+ .that.equals(keenHelper.masterKey);
+ });
+ it("should remove the masterKey by passing null", function(){
+ var client = new Keen({ masterKey: "123" });
+ client.masterKey(null);
+ expect(client.config.masterKey).to.not.exist;
+ });
+ });
+
+});
diff --git a/test/unit/modules/core/events-spec.js b/test/unit/modules/core/events-spec.js
new file mode 100644
index 0000000..3c339a7
--- /dev/null
+++ b/test/unit/modules/core/events-spec.js
@@ -0,0 +1,82 @@
+var chai = require("chai"),
+ expect = require("chai").expect,
+ spies = require("chai-spies");
+
+chai.use(spies);
+
+var Keen = require("../../../../src/core");
+
+describe("Keen.Events system", function(){
+
+ describe("#on", function(){
+ it("should attach custom event listeners with #on", function(){
+ Keen.on("whatever", chai.spy());
+ expect(Keen.listeners()).to.be.an("array");
+ });
+ });
+
+ describe("#trigger", function(){
+ it("should call bound functions when triggered", function(){
+ var callback = chai.spy();
+ Keen.on("whatever", callback);
+ Keen.trigger("whatever");
+ expect(callback).to.have.been.called.once;
+ });
+
+ it("should pass arguments to bound functions when triggered", function(){
+ var callback = chai.spy(),
+ payload = { status: "ok" };
+ Keen.on("whatever", callback);
+ Keen.trigger("whatever", payload);
+ expect(callback).to.have.been.called.once.with(payload);
+ });
+
+ it("should call bound functions multiple when triggered multiple times", function(){
+ var callback = chai.spy();
+ Keen.on("whatever", callback);
+ Keen.trigger("whatever");
+ Keen.trigger("whatever");
+ Keen.trigger("whatever");
+ expect(callback).to.have.been.called.exactly(3);
+ });
+ });
+
+ describe("#off", function(){
+ it("should remove all listeners for an event name with #off(name)", function(){
+ var callback = chai.spy();
+ Keen.on("whatever", callback);
+ Keen.on("whatever", callback);
+ Keen.off("whatever");
+ Keen.trigger("whatever");
+ expect(callback).to.not.have.been.called.once;
+ });
+
+ it("should remove specified listeners with #off(name, callback)", function(){
+ var callback = chai.spy(),
+ fakeback = function(){
+ throw Error("Don't call me!");
+ };
+ Keen.on("whatever", callback);
+ Keen.on("whatever", fakeback);
+ Keen.off("whatever", fakeback);
+ Keen.trigger("whatever");
+ expect(callback).to.have.been.called.once;
+ });
+ });
+
+ describe("#once", function() {
+ it("should call once handlers once when triggered", function(){
+ var query = Keen;
+ var callbackA = chai.spy();
+ var callbackB = chai.spy();
+ Keen.once('event', callbackA);
+ Keen.once('event', callbackB);
+ Keen.trigger('event');
+ expect(callbackA).to.have.been.called.once;
+ expect(callbackB).to.have.been.called.once;
+ Keen.trigger('event');
+ expect(callbackA).to.have.been.called.once;
+ expect(callbackB).to.have.been.called.once;
+ });
+ });
+});
diff --git a/test/unit/query.js b/test/unit/modules/core/query-spec.js
similarity index 55%
rename from test/unit/query.js
rename to test/unit/modules/core/query-spec.js
index 14b8b51..a11e398 100644
--- a/test/unit/query.js
+++ b/test/unit/modules/core/query-spec.js
@@ -1,3 +1,8 @@
+var expect = require("chai").expect;
+
+var Keen = require("../../../../src/core"),
+ keenHelper = require("../../helpers/test-config");
+
describe("Keen.Query", function() {
beforeEach(function() {
@@ -25,10 +30,6 @@ describe("Keen.Query", function() {
expect(this.query).to.have.property("analysis").eql("count");
});
- it("should have a correct path propery", function(){
- expect(this.query).to.have.property("path").eql("/queries/count");
- });
-
it("should have a params object", function(){
expect(this.query).to.have.property("params");
});
@@ -115,82 +116,6 @@ describe("Keen.Query", function() {
});
- describe("#on", function(){
- it("should attach custom event listeners with #on", function(){
- this.query.on("whatever", sinon.spy());
- expect(this.query).to.have.property("listeners")
- .that.is.an("object")
- .that.has.property("whatever")
- .that.is.an("array")
- .with.deep.property('[0]')
- .that.is.an("object")
- .that.has.property("callback");
- });
- });
-
- describe("#trigger", function(){
- it("should call bound functions when triggered", function(){
- var callback = sinon.spy();
- this.query.on("whatever", callback);
- this.query.trigger("whatever");
- expect(callback.calledOnce).to.be.ok;
- });
-
- it("should pass arguments to bound functions when triggered", function(){
- var callback = sinon.spy(),
- payload = { status: "ok" };
- this.query.on("whatever", callback);
- this.query.trigger("whatever", payload);
- expect(callback.calledWith(payload)).to.be.ok;
- });
- it("should call bound functions multiple when triggered multiple times", function(){
- var callback = sinon.spy();
- this.query.on("whatever", callback);
- this.query.trigger("whatever");
- this.query.trigger("whatever");
- this.query.trigger("whatever");
- expect(callback.callCount).to.eql(3);
- });
- });
-
- describe("#off", function(){
- it("should remove all listeners for an event name with #off(name)", function(){
- var callback = sinon.spy();
- this.query.on("whatever", callback);
- this.query.on("whatever", callback);
- this.query.off("whatever");
- this.query.trigger("whatever");
- expect(callback.callCount).to.eql(0);
- });
-
- it("should remove specified listeners with #off(name, callback)", function(){
- var callback = sinon.spy(),
- fakeback = function(){
- throw Error("Don't call me!");
- };
- this.query.on("whatever", callback);
- this.query.on("whatever", fakeback);
- this.query.off("whatever", fakeback);
- this.query.trigger("whatever");
- expect(callback.callCount).to.eql(1);
- });
- });
-
- describe("#once", function() {
- it("should call once handlers once when triggered", function(){
- var query = this.query;
- var callbackA = sinon.spy();
- var callbackB = sinon.spy();
- this.query.once('event', callbackA);
- this.query.once('event', callbackB);
- this.query.trigger('event');
- expect(callbackA.callCount).to.eql(1);
- expect(callbackB.callCount).to.eql(1);
- this.query.trigger('event');
- expect(callbackA.callCount).to.eql(1);
- expect(callbackB.callCount).to.eql(1);
- });
- });
});
diff --git a/test/unit/modules/core/request/browser-spec.js b/test/unit/modules/core/request/browser-spec.js
new file mode 100644
index 0000000..abee028
--- /dev/null
+++ b/test/unit/modules/core/request/browser-spec.js
@@ -0,0 +1,104 @@
+/* globals: sinon */
+var expect = require("chai").expect,
+ JSON2 = require("JSON2");
+
+var Keen = require("../../../../../src/keen"),
+ keenHelper = require("../../../helpers/test-config");
+
+describe("Keen.Request", function() {
+
+ beforeEach(function() {
+ this.client = new Keen({
+ projectId: keenHelper.projectId,
+ readKey: keenHelper.readKey,
+ requestType: "xhr"
+ });
+ this.query = new Keen.Query("count", {
+ eventCollection: "test-collection"
+ });
+ this.postUrl = this.client.url("/queries/count");
+ this.server = sinon.fakeServer.create();
+ });
+
+ afterEach(function(){
+ this.client = void 0;
+ this.query = void 0;
+ this.server.restore();
+ });
+
+ describe(".run method", function(){
+
+ it("should be a method", function(){
+ expect(this.client.run).to.be.a("function");
+ });
+
+ it("should throw an error when passed an invalid object", function(){
+ var self = this;
+ expect(function(){ self.run(null); }).to.throw(Error);
+ expect(function(){ self.run({}); }).to.throw(Error);
+ expect(function(){ self.run(0); }).to.throw(Error);
+ expect(function(){ self.run("string"); }).to.throw(Error);
+ });
+
+ it("should return a response when successful", function(){
+ var response = { result: 1 };
+ this.server.respondWith( "POST", this.postUrl, [ 200, { "Content-Type": "application/json"}, JSON2.stringify(response) ] );
+ this.server.respond();
+ this.client.run(this.query, function(err, res){
+ expect(err).to.be.a("null");
+ expect(res).to.deep.equal(response);
+ });
+ });
+
+ it("should return an error when unsuccessful", function(){
+ var response = { error_code: "ResourceNotFoundError", message: "no foo" };
+ this.server.respondWith( "POST", this.postUrl, [ 500, { "Content-Type": "application/json"}, JSON2.stringify(response) ] );
+ this.server.respond();
+ this.client.run(this.query, function(err, res){
+ expect(err).to.exist;
+ expect(err["code"]).to.equal(response.error_code);
+ expect(res).to.be.a("null");
+ });
+ });
+
+ it("should return an error when timed out", function(){
+ this.server.respondWith( "POST", this.postUrl, [ 500, { "Content-Type": "application/json"}, "" ] );
+ var req = new Keen.Request(this.client, [this.query], function(err, res){
+ expect(err).to.exist;
+ expect(err["message"]).to.equal("timeout of 1ms exceeded");
+ expect(res).to.be.a("null");
+ });
+ req
+ .timeout(1)
+ .refresh();
+ });
+
+ describe("Multiple queries", function(){
+ it("should return a single response when successful", function(){
+ var response = [{ result: 1 }, { result: 1 }, { result: 1 }];
+ this.server.respondWith( "POST", this.postUrl, [ 200, { "Content-Type": "application/json"}, JSON2.stringify(response[0]) ] );
+ this.server.respondWith( "POST", this.postUrl, [ 200, { "Content-Type": "application/json"}, JSON2.stringify(response[1]) ] );
+ this.server.respondWith( "POST", this.postUrl, [ 200, { "Content-Type": "application/json"}, JSON2.stringify(response[2]) ] );
+ this.server.respond();
+ this.client.run([this.query, this.query, this.query], function(err, res){
+ expect(err).to.be.a("null");
+ expect(res).to.be.an("array").and.to.have.length(3);
+ expect(res).to.deep.equal(response);
+ });
+ });
+ it('should return a single error when unsuccessful', function(){
+ var response = { error_code: "ResourceNotFoundError", message: "no foo" };
+ this.server.respondWith( "POST", this.postUrl, [ 500, { "Content-Type": "application/json"}, JSON2.stringify(response) ] );
+ this.server.respondWith( "POST", this.postUrl, [ 200, { "Content-Type": "application/json"}, JSON2.stringify({ result: 1 }) ] );
+ this.server.respondWith( "POST", this.postUrl, [ 200, { "Content-Type": "application/json"}, JSON2.stringify({ result: 1 }) ] );
+ this.client.run([this.query, this.query, this.query], function(err, res){
+ expect(err).to.exist;
+ expect(err["code"]).to.equal(response.error_code);
+ expect(res).to.be.a("null");
+ });
+ });
+ });
+
+ });
+
+});
diff --git a/test/unit/modules/core/request/server-spec.js b/test/unit/modules/core/request/server-spec.js
new file mode 100644
index 0000000..887709f
--- /dev/null
+++ b/test/unit/modules/core/request/server-spec.js
@@ -0,0 +1,105 @@
+var chai = require("chai"),
+ expect = require("chai").expect,
+ JSON2 = require("JSON2"),
+ spies = require("chai-spies");
+
+chai.use(spies);
+
+var Keen = require("../../../../../src/server"),
+ keenHelper = require("../../../helpers/test-config"),
+ mock = require("../../../helpers/mockServerRequests");
+
+describe("Keen.Request", function() {
+
+ beforeEach(function() {
+ this.client = new Keen({
+ projectId: keenHelper.projectId,
+ readKey: keenHelper.readKey
+ });
+ this.query = new Keen.Query("count", {
+ eventCollection: "test-collection"
+ });
+ });
+
+ afterEach(function(){
+ this.client = void 0;
+ this.query = void 0;
+ });
+
+ describe(".run method", function(){
+
+ it("should be a method", function(){
+ expect(this.client.run).to.be.a("function");
+ });
+
+ it("should throw an error when passed an invalid object", function(){
+ var self = this;
+ expect(function(){ self.run(null); }).to.throw(Error);
+ expect(function(){ self.run({}); }).to.throw(Error);
+ expect(function(){ self.run(0); }).to.throw(Error);
+ expect(function(){ self.run("string"); }).to.throw(Error);
+ });
+
+ it("should return a response when successful", function(done){
+ var response = { result: 1 };
+ mock.post("/queries/count", 200, JSON2.stringify(response));
+ this.client.run(this.query, function(err, res){
+ expect(err).to.be.a("null");
+ expect(res).to.deep.equal(response);
+ done();
+ });
+ });
+
+ it("should return an error when unsuccessful", function(done){
+ var response = { error_code: "ResourceNotFoundError", message: "no foo" };
+ mock.post("/queries/count", 500, JSON2.stringify(response));
+ this.client.run(this.query, function(err, res){
+ expect(err).to.exist;
+ expect(err["code"]).to.equal(response.error_code);
+ expect(res).to.be.a("null");
+ done();
+ });
+ });
+
+ it("should return an error when timed out", function(){
+ mock.post("/queries/count", 500, '{ "timeout": 1, "message": "timeout of 1ms exceeded" }', 1000);
+ var req = new Keen.Request(this.client, [this.query], function(err, res){
+ expect(err).to.exist;
+ expect(err["message"]).to.equal("timeout of 1ms exceeded");
+ expect(res).to.be.a("null");
+ });
+ req
+ .timeout(1)
+ .refresh();
+ });
+
+ describe("Multiple queries", function(){
+ it("should return a single response when successful", function(done){
+ var response = [{ result: 1 }, { result: 1 }, { result: 1 }];
+ mock.post("/queries/count", 200, JSON2.stringify(response[0]));
+ mock.post("/queries/count", 200, JSON2.stringify(response[1]));
+ mock.post("/queries/count", 200, JSON2.stringify(response[2]));
+ this.client.run([this.query, this.query, this.query], function(err, res){
+ expect(err).to.be.a("null");
+ expect(res).to.be.an("array").and.to.have.length(3);
+ expect(res).to.deep.equal(response);
+ done();
+ });
+ });
+ it('should return a single error when unsuccessful', function(done){
+ var response = { error_code: "ResourceNotFoundError", message: "no foo" };
+ mock.post("/queries/count", 500, JSON2.stringify(response));
+ mock.post("/queries/count", 200, JSON2.stringify({ result: 1 }));
+ mock.post("/queries/count", 200, JSON2.stringify({ result: 1 }));
+ this.client.run([this.query, this.query, this.query], function(err, res){
+ expect(err).to.exist;
+ expect(err["code"]).to.equal(response.error_code);
+ expect(res).to.be.a("null");
+ done();
+ });
+ });
+ });
+
+ });
+
+});
diff --git a/test/unit/modules/core/tracker/browser-spec.js b/test/unit/modules/core/tracker/browser-spec.js
new file mode 100644
index 0000000..cb154b7
--- /dev/null
+++ b/test/unit/modules/core/tracker/browser-spec.js
@@ -0,0 +1,195 @@
+/* globals: sinon */
+var expect = require("chai").expect,
+ JSON2 = require("JSON2");
+
+var Keen = require("../../../../../src/keen"),
+ keenHelper = require("../../../helpers/test-config");
+
+describe("Tracker (browser)", function() {
+
+ describe("#addEvent", function() {
+
+ describe("Keen.enabled", function(){
+
+ beforeEach(function() {
+ this.client = new Keen({ projectId: "123" });
+ });
+
+ it("should not send events if set to \"false\"", function(){
+ Keen.enabled = false;
+ this.client.addEvent("not-going", { test: "data" }, function(err, res){
+ expect(err).to.exist;
+ expect(res).to.be.null;
+ });
+ Keen.enabled = true;
+ });
+
+ });
+
+ describe("enforce correct arguments for #addEvent", function(){
+
+ beforeEach(function() {
+ this.client = new Keen({ projectId: "123" });
+ });
+
+ it("should return an error message if event collection is omitted", function(){
+ this.client.addEvent(null, { test: "data" }, function(err, res){
+ expect(err).to.exist;
+ expect(res).to.be.null;
+ });
+ });
+
+ });
+
+ describe("via XHR/CORS (if supported)", function(){
+
+ beforeEach(function() {
+ var self = this;
+ self.client = new Keen({
+ projectId: keenHelper.projectId,
+ writeKey: keenHelper.writeKey,
+ // host: keenHelper.host,
+ requestType: "xhr"
+ });
+ self.postUrl = self.client.url("/events/" + keenHelper.collection);
+ self.server = sinon.fakeServer.create();
+ });
+
+ afterEach(function(){
+ this.server.restore();
+ });
+
+ if ('withCredentials' in new XMLHttpRequest()) {
+
+ it("should POST to the API using XHR", function() {
+ var callback = sinon.spy();
+ this.server.respondWith( "POST", this.postUrl, [ 200, { "Content-Type": "application/json"}, keenHelper.responses.success ] );
+ this.client.addEvent(keenHelper.collection, keenHelper.properties, callback);
+ this.server.respond();
+ expect(this.server.responses[0].response[2]).to.equal(keenHelper.responses["success"]);
+ expect(callback.calledOnce).to.be.ok;
+ expect(callback.calledWith( null, JSON.parse(keenHelper.responses["success"]) )).to.be.ok;
+ });
+
+ it("should call the error callback on error", function() {
+ var callback = sinon.spy();
+ this.client.addEvent(keenHelper.collection, keenHelper.properties, function(err, res){
+ expect(err).to.exist;
+ expect(res).to.be.null;
+ });
+ this.server.respondWith( "POST", this.postUrl, [ 500, { "Content-Type": "application/json"}, keenHelper.responses["error"] ] );
+ this.server.respond();
+ expect(this.server.responses[0].response[2]).to.equal(keenHelper.responses["error"]);
+ // expect(callback.calledWith(n)).not.to.be.ok;
+ // expect(callbacks[1].calledOnce).to.be.ok;
+ // expect(callbacks[1].calledWith(JSON.parse(keenHelper.responses["error"]))).to.be.ok;
+ });
+
+ }
+
+ });
+
+ // describe("via JSONP to a fake server", function(){
+ // beforeEach(function() {
+ // this.client = new Keen({
+ // projectId: keenHelper.projectId,
+ // writeKey: keenHelper.writeKey,
+ // host: keenHelper.host,
+ // requestType: 'jsonp'
+ // });
+ // });
+ // });
+ //
+ // describe("via Image Beacon to a fake server", function(){
+ // beforeEach(function() {
+ // this.client = new Keen({
+ // projectId: keenHelper.projectId,
+ // writeKey: keenHelper.writeKey,
+ // host: keenHelper.host,
+ // requestType: 'beacon'
+ // });
+ // });
+ // });
+
+ });
+
+ describe("#addEvents", function() {
+
+ beforeEach(function() {
+ this.client = new Keen({ projectId: "123", writeKey: "2312" });
+ this.batchData = {
+ "pageview": [
+ { page: "this one" },
+ { page: "same!" }
+ ],
+ "click": [
+ { page: "tada!" },
+ { page: "same again" }
+ ]
+ };
+ this.batchResponse = JSON2.stringify({
+ click: [
+ { "success": true }
+ ],
+ pageview: [
+ { "success": true },
+ { "success": true }
+ ]
+ });
+ });
+
+ it("should not send events if Keen.enabled is set to \"false\"", function(){
+ Keen.enabled = false;
+ this.client.addEvents(this.batchData, function(err, res){
+ expect(err).to.exist;
+ expect(res).to.be.null;
+ });
+ Keen.enabled = true;
+ });
+
+ it("should return an error message if first argument is not an object", function(){
+ this.client.addEvents([], function(err, res){
+ expect(err).to.exist;
+ expect(res).to.be.null;
+ });
+ });
+
+ describe("via XHR/CORS (if supported)", function(){
+
+ beforeEach(function() {
+ // var self = this;
+ this.postUrl = this.client.url("/events");
+ this.server = sinon.fakeServer.create();
+ });
+
+ afterEach(function(){
+ this.server.restore();
+ });
+
+ if ('withCredentials' in new XMLHttpRequest()) {
+
+ it("should POST to the API using XHR", function() {
+ var callback = sinon.spy();
+ this.client.addEvents(this.batchData, callback);
+ this.server.respondWith( "POST", this.postUrl, [ 200, { "Content-Type": "application/json"}, keenHelper.responses["error"] ] );
+ this.server.respond();
+ expect(this.server.responses[0].response[2]).to.equal(keenHelper.responses["error"]);
+ expect(callback.calledOnce).to.be.ok;
+ });
+
+ it("should call the error callback on error", function() {
+ var callback = sinon.spy();
+ this.client.addEvents(this.batchData, callback);
+ this.server.respondWith( "POST", this.postUrl, [ 500, { "Content-Type": "application/json"}, keenHelper.responses["error"] ] );
+ this.server.respond();
+ expect(this.server.responses[0].response[2]).to.equal(keenHelper.responses["error"]);
+ expect(callback.calledOnce).to.be.ok;
+ });
+
+ }
+
+ });
+
+ });
+
+});
diff --git a/test/unit/modules/core/tracker/server-spec.js b/test/unit/modules/core/tracker/server-spec.js
new file mode 100644
index 0000000..f83a73d
--- /dev/null
+++ b/test/unit/modules/core/tracker/server-spec.js
@@ -0,0 +1,115 @@
+var chai = require("chai"),
+ expect = require("chai").expect,
+ JSON2 = require("JSON2"),
+ spies = require("chai-spies");
+
+chai.use(spies);
+
+var Keen = require("../../../../../src/server"),
+ keenHelper = require("../../../helpers/test-config"),
+ mock = require("../../../helpers/mockServerRequests");
+
+Keen.debug = true;
+
+describe("Tracking (server)", function() {
+
+ beforeEach(function(){
+ this.client = new Keen({
+ projectId: keenHelper.projectId,
+ writeKey: keenHelper.writeKey
+ });
+ });
+
+ // afterEach(function(){
+ //
+ // });
+
+ describe("#addEvent", function() {
+
+ // it("should pass",function(){
+ // expect(1).to.be.ok;
+ // });
+
+ it("should make an HTTP request",function(done){
+ mock.post("/events/" + keenHelper.collection, 201, keenHelper.responses.success);
+ this.client.addEvent( keenHelper.collection, keenHelper.properties, function(err, res) {
+ expect(err).to.be.null;
+ expect(res).to.deep.equal( JSON2.parse(keenHelper.responses.success) );
+ done();
+ });
+ });
+
+ it("should not make an HTTP request if Keen.enabled is set to \"false\"", function(done){
+ Keen.enabled = false;
+ this.client.addEvent( keenHelper.collection, keenHelper.properties, function(err, res){
+ expect(err).to.exist;
+ expect(res).to.not.exist;
+ done();
+ });
+ Keen.enabled = true;
+ });
+
+ it("should return an error message if event collection is omitted", function(done){
+ // this.client.on("error", spy);
+ this.client.addEvent( null, keenHelper.properties, function(err, res){
+ expect(err).to.exist;
+ expect(res).to.not.exist;
+ done();
+ });
+ // this.client.off("error", spy);
+ });
+
+ });
+
+ describe("#addEvents", function() {
+
+ beforeEach(function() {
+ this.batchData = {
+ "pageview": [
+ { page: "this one" },
+ { page: "same!" }
+ ],
+ "click": [
+ { page: "tada!" },
+ { page: "same again" }
+ ]
+ };
+ this.batchResponse = JSON2.stringify({
+ click: [
+ { "success": true }
+ ],
+ pageview: [
+ { "success": true },
+ { "success": true }
+ ]
+ });
+ });
+
+ it("should make an HTTP request",function(){
+ var self = this;
+ mock.post("/events", 201, self.batchResponse);
+ self.client.addEvents( self.batchData, function(err, res) {
+ expect(err).to.be.null;
+ expect(res).to.deep.equal( JSON2.parse(self.batchResponse) );
+ });
+ });
+
+ it("should not send events if Keen.enabled is set to \"false\"", function(){
+ Keen.enabled = false;
+ this.client.addEvents(this.batchData, function(err, res){
+ expect(err).to.exist;
+ expect(res).to.be.null;
+ });
+ Keen.enabled = true;
+ });
+
+ it("should return an error message if first argument is not an object", function(){
+ this.client.addEvents([], function(err, res){
+ expect(err).to.exist;
+ expect(res).to.be.null;
+ });
+ });
+
+ });
+
+});
diff --git a/test/unit/dataset.js b/test/unit/modules/dataset/dataset-spec.js
similarity index 96%
rename from test/unit/dataset.js
rename to test/unit/modules/dataset/dataset-spec.js
index 7600c4d..2f43294 100644
--- a/test/unit/dataset.js
+++ b/test/unit/modules/dataset/dataset-spec.js
@@ -1,3 +1,19 @@
+var expect = require("chai").expect;
+
+var Keen = require("../../../../src/core"),
+ keenHelper = require("../../helpers/test-config");
+
+var data_extraction = require("./sample-data/extraction"),
+ data_extraction_uneven = require("./sample-data/extraction-uneven")
+ data_metric = require("./sample-data/metric"),
+ data_groupBy = require("./sample-data/groupBy"),
+ data_groupBy_boolean = require("./sample-data/groupBy-boolean"),
+ data_interval_double_groupBy = require("./sample-data/interval-double-groupBy"),
+ data_interval_groupBy_empties = require("./sample-data/interval-groupBy-empties"),
+ data_interval_groupBy_boolean = require("./sample-data/interval-groupBy-boolean"),
+ data_interval_groupBy_nulls = require("./sample-data/interval-groupBy-nulls"),
+ data_funnel = require("./sample-data/funnel");
+
describe("Keen.Dataset", function(){
beforeEach(function(){
@@ -955,19 +971,15 @@ describe("Keen.Dataset", function(){
- describe("#pick", function(){
- it("should return a given index of an array", function(){
- expect(this.ds.pick(["A","B"], 1)).to.eql("B");
- });
- describe("#getRowIndex", function(){
- it("should return the first value of a given row (array)", function(){
- expect(this.ds.getRowIndex(["Index", 0, 1, 2, 3])).to.eql("Index");
- });
+ describe("#getRowIndex", function(){
+ it("should return the first value of a given row (array)", function(){
+ expect(this.ds.getRowIndex(["Index", 0, 1, 2, 3])).to.eql("Index");
});
- describe("#getColumnLabel", function(){
- it("should return the first value of a given column (array)", function(){
- expect(this.ds.getColumnLabel(["Series A", 1, 2, 3, 4,])).to.eql("Series A");
- });
+ });
+
+ describe("#getColumnLabel", function(){
+ it("should return the first value of a given column (array)", function(){
+ expect(this.ds.getColumnLabel(["Series A", 1, 2, 3, 4,])).to.eql("Series A");
});
});
diff --git a/test/unit/data/extraction-uneven.js b/test/unit/modules/dataset/sample-data/extraction-uneven.js
similarity index 98%
rename from test/unit/data/extraction-uneven.js
rename to test/unit/modules/dataset/sample-data/extraction-uneven.js
index 6bf79c5..1772a49 100644
--- a/test/unit/data/extraction-uneven.js
+++ b/test/unit/modules/dataset/sample-data/extraction-uneven.js
@@ -1,4 +1,4 @@
-var data_extraction_uneven = {"result":[
+module.exports = {"result":[
{
"keen": {
"timestamp": "2014-02-12T01:44:25.310Z",
diff --git a/test/unit/data/extraction.js b/test/unit/modules/dataset/sample-data/extraction.js
similarity index 99%
rename from test/unit/data/extraction.js
rename to test/unit/modules/dataset/sample-data/extraction.js
index a99d738..9e84e7c 100644
--- a/test/unit/data/extraction.js
+++ b/test/unit/modules/dataset/sample-data/extraction.js
@@ -1,4 +1,4 @@
-var data_extraction = {
+module.exports = {
"result": [
{
"keen": {
diff --git a/test/unit/data/funnel.js b/test/unit/modules/dataset/sample-data/funnel.js
similarity index 97%
rename from test/unit/data/funnel.js
rename to test/unit/modules/dataset/sample-data/funnel.js
index bd36df4..0be15cf 100644
--- a/test/unit/data/funnel.js
+++ b/test/unit/modules/dataset/sample-data/funnel.js
@@ -1,4 +1,4 @@
-var data_funnel = {
+module.exports = {
"result": [
42,
32,
diff --git a/test/unit/data/groupBy-boolean.js b/test/unit/modules/dataset/sample-data/groupBy-boolean.js
similarity index 86%
rename from test/unit/data/groupBy-boolean.js
rename to test/unit/modules/dataset/sample-data/groupBy-boolean.js
index 402c398..779fc8c 100644
--- a/test/unit/data/groupBy-boolean.js
+++ b/test/unit/modules/dataset/sample-data/groupBy-boolean.js
@@ -1,4 +1,4 @@
-var data_groupBy_boolean = {
+module.exports = {
"result": [
{
"switch": false,
diff --git a/test/unit/data/groupBy.js b/test/unit/modules/dataset/sample-data/groupBy.js
similarity index 99%
rename from test/unit/data/groupBy.js
rename to test/unit/modules/dataset/sample-data/groupBy.js
index 25de4ae..640babe 100644
--- a/test/unit/data/groupBy.js
+++ b/test/unit/modules/dataset/sample-data/groupBy.js
@@ -1,4 +1,4 @@
-var data_groupBy = {
+module.exports = {
"result": [
{
"page": "http://dustinlarimer.com/",
diff --git a/test/unit/data/interval-double-groupBy.js b/test/unit/modules/dataset/sample-data/interval-double-groupBy.js
similarity index 97%
rename from test/unit/data/interval-double-groupBy.js
rename to test/unit/modules/dataset/sample-data/interval-double-groupBy.js
index b63d6ff..4c3ca06 100644
--- a/test/unit/data/interval-double-groupBy.js
+++ b/test/unit/modules/dataset/sample-data/interval-double-groupBy.js
@@ -1,4 +1,4 @@
-var data_interval_double_groupBy = {
+module.exports = {
"result": [
{
"timeframe": { "start":"2014-04-22T07:00:00.000Z", "end":"2014-04-23T07:00:00.000Z" },
diff --git a/test/unit/data/interval-groupBy-boolean.js b/test/unit/modules/dataset/sample-data/interval-groupBy-boolean.js
similarity index 97%
rename from test/unit/data/interval-groupBy-boolean.js
rename to test/unit/modules/dataset/sample-data/interval-groupBy-boolean.js
index a66193f..e4e8f9c 100644
--- a/test/unit/data/interval-groupBy-boolean.js
+++ b/test/unit/modules/dataset/sample-data/interval-groupBy-boolean.js
@@ -1,4 +1,4 @@
-var data_interval_groupBy_boolean = {
+module.exports = {
"result": [
{
"value": [
diff --git a/test/unit/data/interval-groupBy-empties.js b/test/unit/modules/dataset/sample-data/interval-groupBy-empties.js
similarity index 96%
rename from test/unit/data/interval-groupBy-empties.js
rename to test/unit/modules/dataset/sample-data/interval-groupBy-empties.js
index 17453f8..eabbcf3 100644
--- a/test/unit/data/interval-groupBy-empties.js
+++ b/test/unit/modules/dataset/sample-data/interval-groupBy-empties.js
@@ -1,4 +1,4 @@
-var data_interval_groupBy_empties = {
+module.exports = {
"result": [
{
"value": [],
diff --git a/test/unit/data/interval-groupBy-nulls.js b/test/unit/modules/dataset/sample-data/interval-groupBy-nulls.js
similarity index 98%
rename from test/unit/data/interval-groupBy-nulls.js
rename to test/unit/modules/dataset/sample-data/interval-groupBy-nulls.js
index a511971..e2298ef 100644
--- a/test/unit/data/interval-groupBy-nulls.js
+++ b/test/unit/modules/dataset/sample-data/interval-groupBy-nulls.js
@@ -1,4 +1,4 @@
-var data_interval_groupBy_nulls = {
+module.exports = {
"result": [
{
"value": [
diff --git a/test/unit/data/metric.js b/test/unit/modules/dataset/sample-data/metric.js
similarity index 50%
rename from test/unit/data/metric.js
rename to test/unit/modules/dataset/sample-data/metric.js
index e9de324..4f7a134 100644
--- a/test/unit/data/metric.js
+++ b/test/unit/modules/dataset/sample-data/metric.js
@@ -1,3 +1,3 @@
-var data_metric = {
+module.exports = {
"result": 2450
};
diff --git a/test/unit/dataviz.js b/test/unit/modules/dataviz/dataviz-spec.js
similarity index 96%
rename from test/unit/dataviz.js
rename to test/unit/modules/dataviz/dataviz-spec.js
index 19d09c2..0db8c21 100644
--- a/test/unit/dataviz.js
+++ b/test/unit/modules/dataviz/dataviz-spec.js
@@ -1,3 +1,9 @@
+/* globals: sinon */
+var expect = require("chai").expect;
+
+var Keen = require("../../../../src/core"),
+ keenHelper = require("../../helpers/test-config");
+
describe("Keen.Dataviz", function(){
beforeEach(function(){
@@ -315,6 +321,13 @@ describe("Keen.Dataviz", function(){
});
describe("#el", function(){
+
+ beforeEach(function(){
+ var elDiv = document.createElement("div");
+ elDiv.id = "chart-test";
+ document.body.appendChild(elDiv);
+ });
+
it("should return undefined by default", function(){
expect(this.dataviz.el()).to.be.an("undefined");
});
@@ -391,7 +404,15 @@ describe("Keen.Dataviz", function(){
describe("Adapter actions", function(){
beforeEach(function(){
- registerDemoAdapter();
+ Keen.Dataviz.register("demo", {
+ "chart": {
+ initialize: sinon.spy(),
+ render: sinon.spy(),
+ update: sinon.spy(),
+ destroy: sinon.spy(),
+ error: sinon.spy()
+ }
+ });
this.dataviz.adapter({ library: "demo", chartType: "chart" });
});
@@ -455,16 +476,3 @@ describe("Keen.Dataviz", function(){
});
});
-
-
-function registerDemoAdapter(){
- Keen.Dataviz.register("demo", {
- "chart": {
- initialize: sinon.spy(),
- render: sinon.spy(),
- update: sinon.spy(),
- destroy: sinon.spy(),
- error: sinon.spy()
- }
- });
-}
diff --git a/test/unit/modules/server/scoped-keys-spec.js b/test/unit/modules/server/scoped-keys-spec.js
new file mode 100644
index 0000000..06bf558
--- /dev/null
+++ b/test/unit/modules/server/scoped-keys-spec.js
@@ -0,0 +1,51 @@
+var expect = require("chai").expect;
+
+var Keen = require("../../../../src/server"),
+ keenHelper = require("../../helpers/test-config");
+
+describe("Scoped Keys", function(){
+
+ beforeEach(function() {
+ this.client = new Keen({
+ projectId : keenHelper.projectId,
+ masterKey : keenHelper.masterKey
+ });
+ });
+
+ it("should encrypt a scoped key with given options", function() {
+
+ var options = {
+ "allowed_operations": ["read"],
+ "filters": [ {
+ "property_name": "purchase.amount",
+ "operator": "eq",
+ "property_value": 56
+ }, {
+ "property_name": "purchase.name",
+ "operator": "ne",
+ "property_value": "Barbie"
+ }]
+ };
+ // Encrypt a scoped key..
+ var scopedKey = Keen.utils.encryptScopedKey(this.client.masterKey(), options);
+ // ..then decrypt it
+ var decryptedOptions = Keen.utils.decryptScopedKey(this.client.masterKey(), scopedKey);
+ expect(decryptedOptions).to.deep.equal(options);
+ });
+
+ it("should decrypt a scoped key and return the correct options", function() {
+
+ var apiKey = "f5d7c745ba4f437a82db02ca8b416556";
+ var scopedKey = "7b8f357fa55e35efb2f7fa51a03ec2835c5537e57457c5a7c1c40c454fc00d5addef7ed911303fc2fa9648d3ae13e638192b86e90cd88657c9dc5cf03990cbf6eb2a7994513d34789bd25447f3dccaf5a3de3b9cacf6c11ded581e0506fca147ea32c13169787bbf8b4d3b8f2952bc0bea1beae3cfbbeaa1f421be2eac4cc223";
+ var options = {
+ "filters": [{
+ "property_name": "account_id",
+ "operator": "eq",
+ "property_value": "4d9a4c421d011c553e000001"
+ }]
+ };
+ var decryptedOptions = Keen.utils.decryptScopedKey(apiKey, scopedKey);
+ expect(decryptedOptions).to.deep.equal(options);
+ });
+
+});
diff --git a/test/unit/utils.js b/test/unit/modules/utils/utils-spec.js
similarity index 72%
rename from test/unit/utils.js
rename to test/unit/modules/utils/utils-spec.js
index 823d63d..00601ac 100644
--- a/test/unit/utils.js
+++ b/test/unit/modules/utils/utils-spec.js
@@ -7,12 +7,12 @@ describe('Keen.utils', function() {
.and.to.be.a('function');
expect(Keen.utils.parseParams).to.exist
.and.to.be.a('function');
- expect(Keen.utils.prettyNumber).to.exist
- .and.to.be.a('function');
- expect(Keen.utils.loadScript).to.exist
- .and.to.be.a('function');
- expect(Keen.utils.loadStyle).to.exist
- .and.to.be.a('function');
+ // expect(Keen.utils.prettyNumber).to.exist
+ // .and.to.be.a('function');
+ // expect(Keen.utils.loadScript).to.exist
+ // .and.to.be.a('function');
+ // expect(Keen.utils.loadStyle).to.exist
+ // .and.to.be.a('function');
});
// describe('#each', function(){
diff --git a/test/unit/server.js b/test/unit/server.js
new file mode 100644
index 0000000..9aa5397
--- /dev/null
+++ b/test/unit/server.js
@@ -0,0 +1,14 @@
+// -------------------------
+// Common modules
+// -------------------------
+require("./modules/core/client-spec");
+require("./modules/core/events-spec");
+require("./modules/core/query-spec");
+require("./modules/dataset/dataset-spec");
+
+// -------------------------
+// Server-specific modules
+// -------------------------
+require("./modules/core/tracker/server-spec");
+require("./modules/core/request/server-spec");
+require("./modules/server/scoped-keys-spec");
diff --git a/test/unit/track.js b/test/unit/track.js
deleted file mode 100644
index e7b9b7d..0000000
--- a/test/unit/track.js
+++ /dev/null
@@ -1,146 +0,0 @@
-describe("Keen tracking methods", function() {
-
- describe("#addEvent", function() {
-
- describe("Keen.enabled", function(){
-
- beforeEach(function() {
- this.project = new Keen({ projectId: "123" });
- });
-
- it("should not send events if set to \"false\"", function(){
- var success = sinon.spy(),
- error = sinon.spy();
- Keen.enabled = false;
- this.project.addEvent("not-going", { test: "data" }, success, error);
- Keen.enabled = true;
- expect(success.calledOnce).not.to.be.ok;
- expect(error.calledOnce).not.to.be.ok;
- });
-
- });
-
- describe("enforce correct arguments for #addEvent", function(){
-
- beforeEach(function() {
- this.project = new Keen({ projectId: "123" });
- });
-
- it("should return an error message if event collection is omitted", function(){
- var success = sinon.spy(),
- error = sinon.spy();
- this.project.addEvent(null, { test: "data" }, success, error);
- expect(success.calledOnce).not.to.be.ok;
- expect(error.calledOnce).to.be.ok;
- });
-
- });
-
- describe("via XHR/CORS (if supported)", function(){
-
- beforeEach(function() {
- var self = this;
- self.project = new Keen({
- projectId: keenHelper.projectId,
- writeKey: keenHelper.writeKey,
- host: keenHelper.host,
- requestType: 'xhr'
- });
- self.postUrl = self.project.url("/projects/" + self.project.projectId() + "/events/" + keenHelper.collection);
- self.server = sinon.fakeServer.create();
- self.respondWith = function(code, body){
- self.server.respondWith("POST", self.postUrl,
- [code, { "Content-Type": "application/json"}, body]);
- };
- });
-
- afterEach(function(){
- this.server.restore();
- });
-
- if ('withCredentials' in new XMLHttpRequest()) {
-
- it("should post to the API using xhr where CORS is supported", function() {
-
- var callbacks = [sinon.spy(), sinon.spy()];
- this.respondWith(200, keenHelper.responses.success);
- this.project.addEvent(keenHelper.collection, keenHelper.properties, callbacks[0], callbacks[1]);
- this.server.respond();
-
- expect(this.server.requests[0].requestBody)
- .to.equal(JSON.stringify(keenHelper.properties));
- expect(callbacks[0].calledOnce).to.be.ok;
- expect(callbacks[0].calledWith(JSON.parse(keenHelper.responses.success))).to.be.ok;
- expect(callbacks[1].calledOnce).not.to.be.ok;
-
- });
-
- it("should call the error callback on error", function() {
-
- var callbacks = [sinon.spy(), sinon.spy()];
- this.respondWith(500, keenHelper.responses.error);
- this.project.addEvent(keenHelper.collection, keenHelper.properties, callbacks[0], callbacks[1]);
- this.server.respond();
-
- expect(this.server.requests[0].requestBody)
- .to.equal(JSON.stringify(keenHelper.properties));
- expect(callbacks[0].calledOnce).not.to.be.ok;
- expect(callbacks[1].calledOnce).to.be.ok;
-
- });
-
- }
-
- });
-
- describe("via JSONP to a fake server", function(){
-
- beforeEach(function() {
- this.project = new Keen({
- projectId: keenHelper.projectId,
- writeKey: keenHelper.writeKey,
- host: keenHelper.host,
- requestType: 'jsonp'
- });
- });
-
- // it("should add a script tag with a URL that has data and modified params", function(){
- //
- // this.project.addEvent(keenHelper.collection, keenHelper.properties);
- // var tag = document.getElementById("keen-jsonp");
- // expect(tag).to.exist;
- // expect(tag.src).to.contain("data=");
- // expect(tag.src).to.contain("modified=");
- //
- // });
-
- });
-
- describe("via Image Beacon to a fake server", function(){
-
- /*
- beforeEach(function() {
- this.project = new Keen({
- projectId: keenHelper.projectId,
- writeKey: keenHelper.writeKey,
- host: keenHelper.host,
- requestType: 'beacon'
- });
- });
-
- it("should add an image tag", function(){
-
- var callbacks = [function(){ console.log('here'); }, sinon.spy()];
- this.project.addEvent(keenHelper.collection, keenHelper.properties, callbacks[0], callbacks[1]);
-
- var tag = document.getElementById("keen-beacon");
- //expect(tag).to.exist;
- //expect(callbacks[0].calledOnce).to.be.ok;
-
- });
- */
-
- });
-
- });
-});
diff --git a/test/unit/visualization.js b/test/unit/visualization.js
deleted file mode 100644
index b0f976a..0000000
--- a/test/unit/visualization.js
+++ /dev/null
@@ -1,26 +0,0 @@
-describe("Keen.Visualization", function(){
- beforeEach(function(){
- var el = document.getElementById("chart-test");
- this.viz = new Keen.Visualization({ result: 0 }, el);
- });
- afterEach(function(){
- this.viz.destroy();
- this.viz = null;
- Keen.Dataviz.visuals = new Array();
- });
- describe("constructor", function(){
- it("should return a new Keen.Dataviz instance", function(){
- expect(this.viz).to.be.an.instanceof(Keen.Dataviz);
- });
- it("should contain view attributes matching Keen.Visualization.defaults", function(){
- expect(this.viz.attributes()).to.deep.equal(Keen.Visualization.defaults);
- });
- it("should contain view defaults matching Keen.Dataviz.defaults", function(){
- expect(this.viz.view.defaults).to.deep.equal(Keen.Dataviz.defaults);
- });
- it("should be appended to Keen.Dataviz.visuals", function(){
- expect(Keen.Dataviz.visuals).to.have.length(1);
- expect(Keen.Dataviz.visuals[0]).and.to.be.an.instanceof(Keen.Dataviz);
- });
- });
-});
diff --git a/test/assets b/test/vendor
similarity index 100%
rename from test/assets
rename to test/vendor