diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..c13c5f6 --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["es2015"] +} diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..630dfe8 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,11 @@ +{ + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module" + }, + "rules": {}, + "env": { + "browser": true, + "node": true + } +} diff --git a/.gitignore b/.gitignore index 838d35d..a3163ad 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,3 @@ -#project specific -inputs.txt -nbproject/ -node_modules/ -webpack -mocha -repl -dist/ - # Logs logs *.log @@ -14,4 +5,27 @@ logs # Runtime data pids *.pid -*.seed \ No newline at end of file +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +# Commenting this out is preferred by some people, see +# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- +node_modules +bower_components +coverage +tmp + +# Users Environment Variables +.lock-wscript diff --git a/.travis.yml b/.travis.yml index df26f35..b1710f0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,10 @@ language: node_js node_js: - - "6" -before_install: - - rvm install ruby --latest - - gem install zendesk_apps_tools - -script: - - npm test - - if [[ "$TRAVIS_BRANCH" == "master" ]]; then npm run-script build-webpack; fi - - if [[ "$TRAVIS_TAG" == "$RELEASE_TAG" ]]; then zat; fi - + - "4" + - "5" + - "stable" +sudo: false +script: "gulp coverage" after_success: - - echo "Success - Branch($TRAVIS_BRANCH) Pull Request($TRAVIS_PULL_REQUEST) Tag($TRAVIS_TAG)" \ No newline at end of file + - npm install -g codeclimate-test-reporter + - codeclimate-test-reporter < coverage/lcov.info diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..7352a7b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +### [0.0.1](https://github.com/matthewjackowski/custard-moon/releases/tag/v0.0.1) + +- The first release diff --git a/gulp b/gulp new file mode 120000 index 0000000..1271d6b --- /dev/null +++ b/gulp @@ -0,0 +1 @@ +./node_modules/gulp/bin/gulp.js \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..013e276 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,202 @@ +const gulp = require('gulp'); +const loadPlugins = require('gulp-load-plugins'); +const del = require('del'); +const glob = require('glob'); +const path = require('path'); +const isparta = require('isparta'); +const webpack = require('webpack'); +const webpackStream = require('webpack-stream'); + +const Instrumenter = isparta.Instrumenter; +const mochaGlobals = require('./test/setup/.globals'); +const manifest = require('./package.json'); + +// Load all of our Gulp plugins +const $ = loadPlugins(); + +// Gather the library data from `package.json` +const config = manifest.babelBoilerplateOptions; +const mainFile = manifest.main; +const destinationFolder = path.dirname(mainFile); +const exportFileName = path.basename(mainFile, path.extname(mainFile)); + +function cleanDist(done) { + del([destinationFolder]).then(() => done()); +} + +function cleanTmp(done) { + del(['tmp']).then(() => done()); +} + +// Lint a set of files +function lint(files) { + return gulp.src(files) + .pipe($.eslint()) + .pipe($.eslint.format()) + .pipe($.eslint.failAfterError()); +} + +function lintSrc() { + return lint('src/**/*.js'); +} + +function lintTest() { + return lint('test/**/*.js'); +} + +function lintGulpfile() { + return lint('gulpfile.js'); +} + +function build() { + return gulp.src(path.join('src', config.entryFileName)) + .pipe(webpackStream({ + output: { + filename: exportFileName + '.js', + libraryTarget: 'umd', + library: config.mainVarName + }, + // Add your own externals here. For instance, + // { + // jquery: true + // } + // would externalize the `jquery` module. + externals: {}, + module: { + loaders: [ + { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' } + ] + }, + devtool: 'source-map' + })) + .pipe(gulp.dest(destinationFolder)) + .pipe($.filter(['**', '!**/*.js.map'])) + .pipe($.rename(exportFileName + '.min.js')) + .pipe($.sourcemaps.init({ loadMaps: true })) + .pipe($.uglify()) + .pipe($.sourcemaps.write('./')) + .pipe(gulp.dest(destinationFolder)); +} + +function _mocha() { + return gulp.src(['test/setup/node.js', 'test/unit/**/*.js'], {read: false}) + .pipe($.mocha({ + reporter: 'dot', + globals: Object.keys(mochaGlobals.globals), + ignoreLeaks: false + })); +} + +function _registerBabel() { + require('babel-register'); +} + +function test() { + _registerBabel(); + return _mocha(); +} + +function coverage(done) { + _registerBabel(); + gulp.src(['src/**/*.js']) + .pipe($.istanbul({ + instrumenter: Instrumenter, + includeUntested: true + })) + .pipe($.istanbul.hookRequire()) + .on('finish', () => { + return test() + .pipe($.istanbul.writeReports()) + .on('end', done); + }); +} + +const watchFiles = ['src/**/*', 'test/**/*', 'package.json', '**/.eslintrc']; + +// Run the headless unit tests as you make changes. +function watch() { + gulp.watch(watchFiles, ['test']); +} + +function testBrowser() { + // Our testing bundle is made up of our unit tests, which + // should individually load up pieces of our application. + // We also include the browser setup file. + const testFiles = glob.sync('./test/unit/**/*.js'); + const allFiles = ['./test/setup/browser.js'].concat(testFiles); + + // Lets us differentiate between the first build and subsequent builds + var firstBuild = true; + + // This empty stream might seem like a hack, but we need to specify all of our files through + // the `entry` option of webpack. Otherwise, it ignores whatever file(s) are placed in here. + return gulp.src('') + .pipe($.plumber()) + .pipe(webpackStream({ + watch: true, + entry: allFiles, + output: { + filename: '__spec-build.js' + }, + // Externals isn't necessary here since these are for tests. + module: { + loaders: [ + // This is what allows us to author in future JavaScript + { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' }, + // This allows the test setup scripts to load `package.json` + { test: /\.json$/, exclude: /node_modules/, loader: 'json-loader' } + ] + }, + plugins: [ + // By default, webpack does `n=>n` compilation with entry files. This concatenates + // them into a single chunk. + new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 }) + ], + devtool: 'inline-source-map' + }, null, function() { + if (firstBuild) { + $.livereload.listen({port: 35729, host: 'localhost', start: true}); + var watcher = gulp.watch(watchFiles, ['lint']); + } else { + $.livereload.reload('./tmp/__spec-build.js'); + } + firstBuild = false; + })) + .pipe(gulp.dest('./tmp')); +} + +// Remove the built files +gulp.task('clean', cleanDist); + +// Remove our temporary files +gulp.task('clean-tmp', cleanTmp); + +// Lint our source code +gulp.task('lint-src', lintSrc); + +// Lint our test code +gulp.task('lint-test', lintTest); + +// Lint this file +gulp.task('lint-gulpfile', lintGulpfile); + +// Lint everything +gulp.task('lint', ['lint-src', 'lint-test', 'lint-gulpfile']); + +// Build two versions of the library +gulp.task('build', ['lint', 'clean'], build); + +// Lint and run our tests +gulp.task('test', ['lint'], test); + +// Set up coverage and run tests +gulp.task('coverage', ['lint'], coverage); + +// Set up a livereload environment for our spec runner `test/runner.html` +gulp.task('test-browser', ['lint', 'clean-tmp'], testBrowser); + +// Run the headless unit tests as you make changes. +gulp.task('watch', watch); + +// An alias of test +gulp.task('default', ['test']); diff --git a/makefile b/makefile index dc2abfb..b895a18 100644 --- a/makefile +++ b/makefile @@ -4,23 +4,10 @@ init: npm install; ln -s ./node_modules/webpack/bin/webpack.js ./webpack -test: - @NODE_ENV=test ./node_modules/.bin/csslint ./src/app.css - @NODE_ENV=test ./node_modules/.bin/jshint ./src/*.js ./lib/*.js ./bin/*.js || echo - @NODE_ENV=test ./node_modules/mocha/bin/mocha \ - --reporter $(REPORTER) - -test-w: - @NODE_ENV=test ./node_modules/.bin/mocha \ - --reporter $(REPORTER) \ - --watch run: cd ./dist;cat ../inputs.txt | zat server; package: cd ./dist;zat package -build-webpack: - @NODE_ENV=test ./node_modules/webpack/bin/webpack.js - .PHONY: test test-w \ No newline at end of file diff --git a/package.json b/package.json index 52398ec..9d7bda0 100644 --- a/package.json +++ b/package.json @@ -1,31 +1,61 @@ { - "name": "transifex-zendesk-app", - "version": "1.2.0", - "description": "[brief description of the app]", - "main": "app.js", - "directories": { - "test": "test" + "name": "transifex-sync-zendesk", + "version": "1.2.1", + "description": "Sync translations between Zendesk Help Center and Transifex", + "main": "dist/app.js", + "scripts": { + "test": "gulp", + "lint": "gulp lint", + "test-browser": "gulp test-browser", + "watch": "gulp watch", + "build": "gulp build", + "coverage": "gulp coverage" }, - "dependencies": { - "csslint": "0.10.0", - "handlebars": "3.0.3", - "jquery": "2.1.4", - "jshint": "2.7.0", - "mocha": "2.2.4", - "mocha-loader": "^0.7.1", - "npmignore": "^0.2.0", - "should": "6.0.1", - "sinon": "1.14.1", - "spahql": "0.7.6", - "tv4": "1.1.9", - "underscore": "1.8.3", - "webpack": "^1.13.1", - "webpack-dev-server": "^1.14.1" + "repository": { + "type": "git", + "url": "https://github.com/matthewjackowski/custard-moon.git" }, - "scripts": { - "test": "make test", - "build-webpack": "make build-webpack" + "keywords": [], + "author": "Matthew Jackowski ", + "license": "Apache 2", + "bugs": { + "url": "https://github.com/transifex/transifex-sync-zendesk/issues" + }, + "homepage": "https://github.com/transifex/transifex-sync-zendesk#tx-sync-javascript-application-for-zendesk", + "devDependencies": { + "babel-cli": "^6.11.4", + "babel-core": "^6.3.26", + "babel-loader": "^6.2.0", + "babel-plugin-add-module-exports": "^0.2.1", + "babel-polyfill": "^6.3.14", + "babel-preset-es2015": "^6.3.13", + "babel-register": "^6.3.13", + "chai": "^3.4.1", + "del": "^2.2.0", + "glob": "^7.0.3", + "gulp": "^3.9.0", + "gulp-eslint": "^3.0.0", + "gulp-filter": "^4.0.0", + "gulp-istanbul": "^1.0.0", + "gulp-livereload": "^3.8.1", + "gulp-load-plugins": "^1.1.0", + "gulp-mocha": "^2.2.0", + "gulp-plumber": "^1.0.1", + "gulp-rename": "^1.2.2", + "gulp-sourcemaps": "^1.6.0", + "gulp-uglify": "^1.5.1", + "isparta": "^4.0.0", + "json-loader": "^0.5.3", + "mocha": "^2.3.4", + "object-assign": "^4.1.0", + "shortid": "^2.2.6", + "sinon": "^1.17.2", + "sinon-chai": "^2.8.0", + "webpack": "^1.12.9", + "webpack-stream": "^3.1.0" }, - "author": "", - "license": "Apache" -} \ No newline at end of file + "babelBoilerplateOptions": { + "entryFileName": "custard-moon.js", + "mainVarName": "main" + } +} diff --git a/src/custard-moon.js b/src/custard-moon.js new file mode 100644 index 0000000..bfb0e16 --- /dev/null +++ b/src/custard-moon.js @@ -0,0 +1,7 @@ +const main = { + greet() { + return 'hello'; + } +}; + +export default main; diff --git a/lib/common.js b/src/lib/common.js similarity index 100% rename from lib/common.js rename to src/lib/common.js diff --git a/lib/messages.js b/src/lib/messages.js similarity index 100% rename from lib/messages.js rename to src/lib/messages.js diff --git a/lib/syncUtil.js b/src/lib/syncUtil.js similarity index 100% rename from lib/syncUtil.js rename to src/lib/syncUtil.js diff --git a/src/lib/transifex-api/md5.js b/src/lib/transifex-api/md5.js new file mode 100755 index 0000000..9a4f8a7 --- /dev/null +++ b/src/lib/transifex-api/md5.js @@ -0,0 +1,193 @@ +var rotateLeft = function(lValue, iShiftBits) { + return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits)); +} + +var addUnsigned = function(lX, lY) { + var lX4, lY4, lX8, lY8, lResult; + lX8 = (lX & 0x80000000); + lY8 = (lY & 0x80000000); + lX4 = (lX & 0x40000000); + lY4 = (lY & 0x40000000); + lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF); + if (lX4 & lY4) return (lResult ^ 0x80000000 ^ lX8 ^ lY8); + if (lX4 | lY4) { + if (lResult & 0x40000000) return (lResult ^ 0xC0000000 ^ lX8 ^ lY8); + else return (lResult ^ 0x40000000 ^ lX8 ^ lY8); + } else { + return (lResult ^ lX8 ^ lY8); + } +} + +var F = function(x, y, z) { + return (x & y) | ((~ x) & z); +} + +var G = function(x, y, z) { + return (x & z) | (y & (~ z)); +} + +var H = function(x, y, z) { + return (x ^ y ^ z); +} + +var I = function(x, y, z) { + return (y ^ (x | (~ z))); +} + +var FF = function(a, b, c, d, x, s, ac) { + a = addUnsigned(a, addUnsigned(addUnsigned(F(b, c, d), x), ac)); + return addUnsigned(rotateLeft(a, s), b); +}; + +var GG = function(a, b, c, d, x, s, ac) { + a = addUnsigned(a, addUnsigned(addUnsigned(G(b, c, d), x), ac)); + return addUnsigned(rotateLeft(a, s), b); +}; + +var HH = function(a, b, c, d, x, s, ac) { + a = addUnsigned(a, addUnsigned(addUnsigned(H(b, c, d), x), ac)); + return addUnsigned(rotateLeft(a, s), b); +}; + +var II = function(a, b, c, d, x, s, ac) { + a = addUnsigned(a, addUnsigned(addUnsigned(I(b, c, d), x), ac)); + return addUnsigned(rotateLeft(a, s), b); +}; + +var convertToWordArray = function(string) { + var lWordCount; + var lMessageLength = string.length; + var lNumberOfWordsTempOne = lMessageLength + 8; + var lNumberOfWordsTempTwo = (lNumberOfWordsTempOne - (lNumberOfWordsTempOne % 64)) / 64; + var lNumberOfWords = (lNumberOfWordsTempTwo + 1) * 16; + var lWordArray = Array(lNumberOfWords - 1); + var lBytePosition = 0; + var lByteCount = 0; + while (lByteCount < lMessageLength) { + lWordCount = (lByteCount - (lByteCount % 4)) / 4; + lBytePosition = (lByteCount % 4) * 8; + lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition)); + lByteCount++; + } + lWordCount = (lByteCount - (lByteCount % 4)) / 4; + lBytePosition = (lByteCount % 4) * 8; + lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition); + lWordArray[lNumberOfWords - 2] = lMessageLength << 3; + lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29; + return lWordArray; +}; + +var wordToHex = function(lValue) { + var WordToHexValue = "", WordToHexValueTemp = "", lByte, lCount; + for (lCount = 0; lCount <= 3; lCount++) { + lByte = (lValue >>> (lCount * 8)) & 255; + WordToHexValueTemp = "0" + lByte.toString(16); + WordToHexValue = WordToHexValue + WordToHexValueTemp.substr(WordToHexValueTemp.length - 2, 2); + } + return WordToHexValue; +}; + +/* istanbul ignore next */ +var uTF8Encode = function(string) { + string = string.replace(/\x0d\x0a/g, "\x0a"); + var output = ""; + for (var n = 0; n < string.length; n++) { + var c = string.charCodeAt(n); + if (c < 128) { + output += String.fromCharCode(c); + } else if ((c > 127) && (c < 2048)) { + output += String.fromCharCode((c >> 6) | 192); + output += String.fromCharCode((c & 63) | 128); + } else { + output += String.fromCharCode((c >> 12) | 224); + output += String.fromCharCode(((c >> 6) & 63) | 128); + output += String.fromCharCode((c & 63) | 128); + } + } + return output; +}; + +export default function(string) { + var x = Array(); + var k, AA, BB, CC, DD, a, b, c, d; + var S11=7, S12=12, S13=17, S14=22; + var S21=5, S22=9 , S23=14, S24=20; + var S31=4, S32=11, S33=16, S34=23; + var S41=6, S42=10, S43=15, S44=21; + string = uTF8Encode(string); + x = convertToWordArray(string); + a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476; + for (k = 0; k < x.length; k += 16) { + AA = a; BB = b; CC = c; DD = d; + a = FF(a, b, c, d, x[k+0], S11, 0xD76AA478); + d = FF(d, a, b, c, x[k+1], S12, 0xE8C7B756); + c = FF(c, d, a, b, x[k+2], S13, 0x242070DB); + b = FF(b, c, d, a, x[k+3], S14, 0xC1BDCEEE); + a = FF(a, b, c, d, x[k+4], S11, 0xF57C0FAF); + d = FF(d, a, b, c, x[k+5], S12, 0x4787C62A); + c = FF(c, d, a, b, x[k+6], S13, 0xA8304613); + b = FF(b, c, d, a, x[k+7], S14, 0xFD469501); + a = FF(a, b, c, d, x[k+8], S11, 0x698098D8); + d = FF(d, a, b, c, x[k+9], S12, 0x8B44F7AF); + c = FF(c, d, a, b, x[k+10], S13, 0xFFFF5BB1); + b = FF(b, c, d, a, x[k+11], S14, 0x895CD7BE); + a = FF(a, b, c, d, x[k+12], S11, 0x6B901122); + d = FF(d, a, b, c, x[k+13], S12, 0xFD987193); + c = FF(c, d, a, b, x[k+14], S13, 0xA679438E); + b = FF(b, c, d, a, x[k+15], S14, 0x49B40821); + a = GG(a, b, c, d, x[k+1], S21, 0xF61E2562); + d = GG(d, a, b, c, x[k+6], S22, 0xC040B340); + c = GG(c, d, a, b, x[k+11], S23, 0x265E5A51); + b = GG(b, c, d, a, x[k+0], S24, 0xE9B6C7AA); + a = GG(a, b, c, d, x[k+5], S21, 0xD62F105D); + d = GG(d, a, b, c, x[k+10], S22, 0x2441453); + c = GG(c, d, a, b, x[k+15], S23, 0xD8A1E681); + b = GG(b, c, d, a, x[k+4], S24, 0xE7D3FBC8); + a = GG(a, b, c, d, x[k+9], S21, 0x21E1CDE6); + d = GG(d, a, b, c, x[k+14], S22, 0xC33707D6); + c = GG(c, d, a, b, x[k+3], S23, 0xF4D50D87); + b = GG(b, c, d, a, x[k+8], S24, 0x455A14ED); + a = GG(a, b, c, d, x[k+13], S21, 0xA9E3E905); + d = GG(d, a, b, c, x[k+2], S22, 0xFCEFA3F8); + c = GG(c, d, a, b, x[k+7], S23, 0x676F02D9); + b = GG(b, c, d, a, x[k+12], S24, 0x8D2A4C8A); + a = HH(a, b, c, d, x[k+5], S31, 0xFFFA3942); + d = HH(d, a, b, c, x[k+8], S32, 0x8771F681); + c = HH(c, d, a, b, x[k+11], S33, 0x6D9D6122); + b = HH(b, c, d, a, x[k+14], S34, 0xFDE5380C); + a = HH(a, b, c, d, x[k+1], S31, 0xA4BEEA44); + d = HH(d, a, b, c, x[k+4], S32, 0x4BDECFA9); + c = HH(c, d, a, b, x[k+7], S33, 0xF6BB4B60); + b = HH(b, c, d, a, x[k+10], S34, 0xBEBFBC70); + a = HH(a, b, c, d, x[k+13], S31, 0x289B7EC6); + d = HH(d, a, b, c, x[k+0], S32, 0xEAA127FA); + c = HH(c, d, a, b, x[k+3], S33, 0xD4EF3085); + b = HH(b, c, d, a, x[k+6], S34, 0x4881D05); + a = HH(a, b, c, d, x[k+9], S31, 0xD9D4D039); + d = HH(d, a, b, c, x[k+12], S32, 0xE6DB99E5); + c = HH(c, d, a, b, x[k+15], S33, 0x1FA27CF8); + b = HH(b, c, d, a, x[k+2], S34, 0xC4AC5665); + a = II(a, b, c, d, x[k+0], S41, 0xF4292244); + d = II(d, a, b, c, x[k+7], S42, 0x432AFF97); + c = II(c, d, a, b, x[k+14], S43, 0xAB9423A7); + b = II(b, c, d, a, x[k+5], S44, 0xFC93A039); + a = II(a, b, c, d, x[k+12], S41, 0x655B59C3); + d = II(d, a, b, c, x[k+3], S42, 0x8F0CCC92); + c = II(c, d, a, b, x[k+10], S43, 0xFFEFF47D); + b = II(b, c, d, a, x[k+1], S44, 0x85845DD1); + a = II(a, b, c, d, x[k+8], S41, 0x6FA87E4F); + d = II(d, a, b, c, x[k+15], S42, 0xFE2CE6E0); + c = II(c, d, a, b, x[k+6], S43, 0xA3014314); + b = II(b, c, d, a, x[k+13], S44, 0x4E0811A1); + a = II(a, b, c, d, x[k+4], S41, 0xF7537E82); + d = II(d, a, b, c, x[k+11], S42, 0xBD3AF235); + c = II(c, d, a, b, x[k+2], S43, 0x2AD7D2BB); + b = II(b, c, d, a, x[k+9], S44, 0xEB86D391); + a = addUnsigned(a, AA); + b = addUnsigned(b, BB); + c = addUnsigned(c, CC); + d = addUnsigned(d, DD); + } + var tempValue = wordToHex(a) + wordToHex(b) + wordToHex(c) + wordToHex(d); + return tempValue.toLowerCase(); +} diff --git a/src/lib/transifex-api/mixins/language.js b/src/lib/transifex-api/mixins/language.js new file mode 100755 index 0000000..c6d73dd --- /dev/null +++ b/src/lib/transifex-api/mixins/language.js @@ -0,0 +1,104 @@ +/** + * The language mixin is responsible for retrieving and setting the languages of + * a project. + * @module mixins/language + */ +export default { + + /** + * Retrieve the target languages of the project. + * @param {string} project_slug - The projects slug + * @example txApi.languages('testproject').then(function(data) {}) + **/ + + languages(project_slug) { + var path = this.urls['languages'] + .replace('', project_slug) + return this.request + .get(path) + .auth(this.username, this.password) + .end() + }, + + /** + * Add a new target language to the project. + * @param {string} project_slug - The projects slug + * @param {object} form - An object containing the target language code and the coordinators array + * @example + * txApi.languageCreate('testproject', { + * language_code: 'en', + * coordinators: [ + * 'alexapi1' + * ] + * }) + **/ + + languageCreate(project_slug, form) { + var path = this.urls['languages'] + .replace('', project_slug) + return this.request + .post(path) + .auth(this.username, this.password) + .send(form) + .end() + }, + + /** + * Retrieve the details of a project's target language. + * @param {string} project_slug - The projects slug + * @param {string} language_code - The target language code + * @example txApi.languageRead('testproject','en').then(function(data) {}); + **/ + + languageRead(project_slug, language_code) { + var path = this.urls['language'] + .replace('', project_slug) + .replace('', language_code) + return this.request + .get(path) + .auth(this.username, this.password) + .end() + }, + + /** + * Update a project's target language. + * @param {string} project_slug - The projects slug + * @param {string} language_code - The target language code + * @param {object} form - An object containing the target language code and the coordinators array + * @example + * txApi.languageUpdate('testproject', 'en', { + * list: 'mylist@listserver.com', + * coordinators: [ + * 'alexapi1' + * ] + * }) + **/ + + languageUpdate(project_slug, language_code, form) { + var path = this.urls['language'] + .replace('', project_slug) + .replace('', language_code); + return this.request + .put(path) + .auth(this.username, this.password) + .send(form) + .end() + }, + + /** + * Remove a target language from the project + * @param {string} project_slug - The projects slug + * @param {string} language_code - The target language code + * @example txApi.languageDelete('testproject','en') + **/ + + languageDelete(project_slug, language_code) { + var path = this.urls['language'] + .replace('', project_slug) + .replace('', language_code); + return this.request + .del(path) + .auth(this.username, this.password) + .end() + } +} diff --git a/src/lib/transifex-api/mixins/languageInfo.js b/src/lib/transifex-api/mixins/languageInfo.js new file mode 100755 index 0000000..d98807f --- /dev/null +++ b/src/lib/transifex-api/mixins/languageInfo.js @@ -0,0 +1,35 @@ +/** + * The languageInfo mixin is responsible for retrieving the list of all languagesInfo + * supported by Transifex, as well a details objects for each supported language. + * @module mixins/languageInfo + */ +export default { + + /** + * Retrieve the list of languages supported by Transifex. + * @example txApi.languagesInfo().then(function(data) {}) + **/ + + languagesInfo() { + var path = this.urls['languagesInfo'] + return this.request + .get(path) + .auth(this.username, this.password) + .end() + }, + + /** + * Retrieve the details of specific language. + * @param {string} language_code - The target language code + * @example txApi.languageInfo('testproject').then(function(data) {}) + **/ + + languageInfo(language_code) { + var path = this.urls['languageInfo'] + .replace('', language_code) + return this.request + .get(path) + .auth(this.username, this.password) + .end() + }, +} diff --git a/src/lib/transifex-api/mixins/project.js b/src/lib/transifex-api/mixins/project.js new file mode 100755 index 0000000..ba2d639 --- /dev/null +++ b/src/lib/transifex-api/mixins/project.js @@ -0,0 +1,95 @@ +/** + * The project mixin is responsible creating, retrieving and updating TransifexApi + * projects. + * @module mixins/project + */ + +export default { + + /** + * Retrieve the projects that the user participates or owns. + * @example txApi.projects().then(function(data) {}) + **/ + + projects() { + var path = this.urls['projects']; + return this.request + .get(path) + .auth(this.username, this.password) + .end() + }, + + /** + * Create a new project + * @param {object} form - An object containing the projects definition + * @example + txApi.projectCreate({ + name: 'autotest', + slug: 'autotest', + private: true, + description: 'this is an automated test', + source_language_code: 'af' + }) + **/ + + projectCreate(form) { + var path = this.urls['projects']; + return this.request + .post(path) + .auth(this.username, this.password) + .send(form) + .end() + }, + + /** + * Retrieve the details of a specific project + * @param {string} project_slug - The projects slug + * @example txApi.projectRead('test_project').then(function(data) {}) + **/ + + projectRead(project_slug) { + var path = this.urls['project'] + .replace('', project_slug); + return this.request + .get(path) + .auth(this.username, this.password) + .end() + }, + + /** + * Update an existing project + * @param {string} project_slug - The projects slug + * @param {object} form - An object containing the projects update + * @example + txApi.projectUpdate('testproject', { + description: 'this is an updated description', + }) + **/ + + projectUpdate(project_slug, form) { + var path = this.urls['project'] + .replace('', project_slug); + return this.request + .put(path) + .auth(this.username, this.password) + .send(form) + .end() + }, + + /** + * Delete an existing project + * @param {string} project_slug - The projects slug + * @example + txApi.projectDelete('testproject') + **/ + + projectDelete(project_slug) { + var path = this.urls['project'] + .replace('', project_slug); + return this.request + .del(path) + .auth(this.username, this.password) + .send({slug: project_slug}) + .end() + } +} diff --git a/src/lib/transifex-api/mixins/resource.js b/src/lib/transifex-api/mixins/resource.js new file mode 100755 index 0000000..e8fa9a5 --- /dev/null +++ b/src/lib/transifex-api/mixins/resource.js @@ -0,0 +1,105 @@ +/** + * The resource mixin is responsible for adding, retrieving and updating resources + * from an existing project. + * @module mixins/resource + */ + +export default { + + /** + * Retrieve the resources of the specified project. + * @param {string} project_slug - The projects slug + * @example txApi.resources().then(function(data) {}) + **/ + + resources(project_slug) { + var path = this.urls['resources'] + .replace('', project_slug) + return this.request + .get(path) + .auth(this.username, this.password) + .end() + }, + + /** + * Add a new resource to a project + * @param {string} project_slug - The projects slug + * @param {object} form - An object containing the resource definition + * @example + txApi.resourceCreate('autotest', { + slug: 'resourcetest', + name: 'resourcetest', + i18n_type: 'KEYVALUEJSON', + content: JSON.stringify({"hello world": "hello world"}), + }) + **/ + + resourceCreate(project_slug, form) { + var path = this.urls['resources'] + .replace('', project_slug) + return this.request + .post(path) + .auth(this.username, this.password) + .send(form) + .end() + }, + + /** + * Retrieve the details of a project's resource + * @param {string} project_slug - The projects slug + * @param {string} resource_slug - The resource slug + * @example txApi.resourceRead('autotest', 'resourcetest') + **/ + + resourceRead(project_slug, resource_slug) { + //if (!resource_slug) Promise.resolve(); + var path = this.urls['resource'] + .replace('', project_slug) + .replace('', resource_slug); + return this.request + .get(path) + .auth(this.username, this.password) + .end() + }, + + /** + * Update an existing resource + * @param {string} project_slug - The projects slug + * @param {string} resource_slug - The resource slug + * @param {object} form - An object containing the resource definition + * @example + txApi.resourceUpdate('autotest', 'resourcetest', { + name: 'updatedresourcetest', + }) + **/ + + resourceUpdate(project_slug, resource_slug, form) { + var path = this.urls['resource'] + .replace('', project_slug) + .replace('', resource_slug); + return this.request + .put(path) + .auth(this.username, this.password) + .send(form) + .end() + }, + + /** + * Delete an existing resource + * @param {string} project_slug - The projects slug + * @param {string} resource_slug - The resource slug + * @example + txApi.resourceDelete('autotest', 'resourcetest) + **/ + + resourceDelete(project_slug, resource_slug) { + var path = this.urls['resource'] + .replace('', project_slug) + .replace('', resource_slug); + return this.request + .del(path) + .auth(this.username, this.password) + .send({slug: resource_slug}) + .end() + } +} diff --git a/src/lib/transifex-api/mixins/resourceString.js b/src/lib/transifex-api/mixins/resourceString.js new file mode 100755 index 0000000..2206a9e --- /dev/null +++ b/src/lib/transifex-api/mixins/resourceString.js @@ -0,0 +1,53 @@ +/** + * The resourceString mixin is responsible for retrieving and updating specific + * source strings that belong to a given resource. + * @module mixins/project + */ + +export default { + + /** + * Retrieve the details of a specific source string + * @param {string} project_slug - The projects slug + * @param {string} resource_slug - The resource slug + * @param {string} source_string - The source string + * @example txApi.resourceStringRead('autotest', 'resourcetest', 'hello world') + **/ + + resourceStringRead(project_slug, resource_slug, source_string) { + source_string = this.strToHash(source_string); + var path = this.urls['resourceString'] + .replace('', project_slug) + .replace('', resource_slug) + .replace('', source_string); + return this.request + .get(path) + .auth(this.username, this.password) + .end() + }, + + /** + * Update an existing source string + * @param {string} project_slug - The projects slug + * @param {string} resource_slug - The resource slug + * @param {string} source_string - The source string + * @param {object} form - An object containing the source string's update + * @example + txApi.resourceStringUpdate('autotest', 'resourcetest', 'hello world', { + comment: 'This is a comment' + }) + **/ + + resourceStringUpdate(project_slug, resource_slug, source_string, form) { + source_string = this.strToHash(source_string); + var path = this.urls['resourceString'] + .replace('', project_slug) + .replace('', resource_slug) + .replace('', source_string); + return this.request + .put(path) + .auth(this.username, this.password) + .send(form) + .end() + } +} diff --git a/src/lib/transifex-api/mixins/translation.js b/src/lib/transifex-api/mixins/translation.js new file mode 100755 index 0000000..9aee615 --- /dev/null +++ b/src/lib/transifex-api/mixins/translation.js @@ -0,0 +1,68 @@ +import Promise from '../polyfills/promise.js'; + +/** + * The translation mixin is responsible for retrieving and uploading translation + * files. + * @module mixins/translation + */ + +export default { + + /** + * Retrieve a resource translation for the given language and project + * @param {string} project_slug - The projects slug + * @param {string} resource_slug - The resource slug + * @param {string} language_code - The target language code + * @example txApi.translationRead('autotest', 'resourcetest', 'en') + **/ + + translationRead(project_slug, resource_slug, language_code) { + var path = this.urls['translation'] + .replace('', project_slug) + .replace('', resource_slug) + .replace('', language_code); + + var that = this; + return new Promise(function(resolve, reject) { + that.request + .get(path) + .auth(that.username, that.password) + .type('application/json') + .end(function(err, res) { + /* istanbul ignore next */ + if (err) return reject(err); + if (res.body && res.body.content) { + res.body.content = JSON.parse(res.body.content); + } + return resolve(res) + }) + }) + }, + + /** + * Update a resource translation for the given language and project + * @param {string} project_slug - The projects slug + * @param {string} resource_slug - The resource slug + * @param {string} language_code - The target language code + * @param {object} form - An object containing the updated translation + * @example + * txApi.translationUpdate('autotest', 'resourcetest', 'en', { + * 'hello world': 'derp' + * }) + **/ + + translationUpdate(project_slug, resource_slug, language_code, form) { + var path = this.urls['translation'] + .replace('', project_slug) + .replace('', resource_slug) + .replace('', language_code); + + return this.request + .put(path) + .auth(this.username, this.password) + .send({ + content: JSON.stringify(form) + }) + .end() + } +} diff --git a/src/lib/transifex-api/mixins/translationString.js b/src/lib/transifex-api/mixins/translationString.js new file mode 100755 index 0000000..3e2e26e --- /dev/null +++ b/src/lib/transifex-api/mixins/translationString.js @@ -0,0 +1,75 @@ +/** + * The translationString mixin is responsible for retrieving and updating specific + * translation strings from a given resource and target language + * @module mixins/translationString + */ +export default { + + /** + * Retrieve a list of all translation for the given language and project + * @param {string} project_slug - The projects slug + * @param {string} resource_slug - The resource slug + * @param {string} language_code - The target language code + * @example txApi.translationStrings('autotest', 'resourcetest', 'en') + **/ + + translationStrings(project_slug, resource_slug, language_code) { + var path = this.urls['translationStrings'] + .replace('', project_slug) + .replace('', resource_slug) + .replace('', language_code); + + return this.request + .get(path) + .auth(this.username, this.password) + .end() + + }, + + /** + * Retrieve a specifiv translation along with its details for the given language and project + * @param {string} project_slug - The projects slug + * @param {string} resource_slug - The resource slug + * @param {string} language_code - The target language code + * @param {string} source_string - The source string + * @example txApi.translationStringRead('autotest', 'resourcetest', 'en') + **/ + + translationStringRead(project_slug, resource_slug, language_code, source_string ) { + source_string = this.strToHash(source_string); + var path = this.urls['translationString'] + .replace('', project_slug) + .replace('', resource_slug) + .replace('', language_code) + .replace('', source_string); + return this.request + .get(path) + .auth(this.username, this.password) + .end() + }, + + /** + * Update a specific translation for the given language and project + * @param {string} project_slug - The projects slug + * @param {string} resource_slug - The resource slug + * @param {string} language_code - The target language code + * @param {string} source_string - The source string + * @param {object} form - An object containing the updated translation + * @example txApi.translationStringRead('autotest', 'resourcetest', 'en') + **/ + + translationStringUpdate(project_slug, resource_slug, language_code, source_string, form) { + source_string = this.strToHash(source_string); + var path = this.urls['translationString'] + .replace('', project_slug) + .replace('', resource_slug) + .replace('', language_code) + .replace('', source_string); + return this.request + .put(path) + .send(form) + .auth(this.username, this.password) + .end() + } + +} diff --git a/src/lib/transifex-api/polyfills/object-assign.js b/src/lib/transifex-api/polyfills/object-assign.js new file mode 100644 index 0000000..349ec24 --- /dev/null +++ b/src/lib/transifex-api/polyfills/object-assign.js @@ -0,0 +1,6 @@ +const objectAssign = require('object-assign'); +module.exports = { + objectAssign: function() { + return objectAssign + } +} \ No newline at end of file diff --git a/src/lib/transifex-api/polyfills/promise.js b/src/lib/transifex-api/polyfills/promise.js new file mode 100644 index 0000000..1df103a --- /dev/null +++ b/src/lib/transifex-api/polyfills/promise.js @@ -0,0 +1,3 @@ +module.exports = { + +} \ No newline at end of file diff --git a/src/lib/transifex-api/polyfills/superagent-promise.js b/src/lib/transifex-api/polyfills/superagent-promise.js new file mode 100644 index 0000000..7c6d6c7 --- /dev/null +++ b/src/lib/transifex-api/polyfills/superagent-promise.js @@ -0,0 +1 @@ +module.exports = {} \ No newline at end of file diff --git a/src/lib/transifex-api/polyfills/superagent.js b/src/lib/transifex-api/polyfills/superagent.js new file mode 100644 index 0000000..7c6d6c7 --- /dev/null +++ b/src/lib/transifex-api/polyfills/superagent.js @@ -0,0 +1 @@ +module.exports = {} \ No newline at end of file diff --git a/src/lib/transifex-api/transifex-api.js b/src/lib/transifex-api/transifex-api.js new file mode 100755 index 0000000..116eb7d --- /dev/null +++ b/src/lib/transifex-api/transifex-api.js @@ -0,0 +1,57 @@ +import Promise from './polyfills/promise'; +import superagent from './polyfills/superagent'; +import superagentpromise from './polyfills/superagent-promise'; +var objectAssign = require('./polyfills/object-assign.js').objectAssign; + +import urlMap from './url.js'; +import md5 from './md5.js'; + +import projectMixin from './mixins/project'; +import resourceMixin from './mixins/resource'; +import resourceStringMixin from './mixins/resourceString'; +import languageMixin from './mixins/language'; +import translationMixin from './mixins/translation'; +import languageInfoMixin from './mixins/languageInfo'; +import translationStringMixin from './mixins/translationString'; + +/** + * Implements a client to the Transifex Api + * + * @class transifexApi + */ +const TransifexApi = { + + /** + * Returns an instantiated api object. + * @param {object} options - A hash containing username and password + * @example txApi = transifexApi.connect({username: password:}) + **/ + + connect(options) { + this.username = options.username; + this.password = options.password; + this.base_url = options.base_url || 'https://www.transifex.com/'; + this.api_prefix = options.api_prefix || 'api/2'; + this.urls = urlMap(`${this.base_url}${this.api_prefix}`); + return this; + }, + strToHash(source_string) { + return this.md5(unescape(encodeURIComponent(source_string + ':'))) + } +} + +//TransifexApi.request = superagentpromise(superagent, Promise); +TransifexApi.request = {} +TransifexApi.md5 = md5; + +TransifexApi.api = objectAssign(TransifexApi, + projectMixin, + resourceMixin, + resourceStringMixin, + languageMixin, + translationMixin, + languageInfoMixin, + translationStringMixin +) + +export default TransifexApi diff --git a/src/lib/transifex-api/url.js b/src/lib/transifex-api/url.js new file mode 100755 index 0000000..51086e5 --- /dev/null +++ b/src/lib/transifex-api/url.js @@ -0,0 +1,20 @@ +export default function(api) { + return { + projects: `${api}/projects`, + project: `${api}/project/`, + + resources: `${api}/project//resources`, + resource: `${api}/project//resource/`, + resourceString: `${api}/project//resource//source/`, + + languages: `${api}/project//languages`, + language: `${api}/project//language/`, + + languagesInfo: `${api}/languages`, + languageInfo: `${api}/language/`, + + translation: `${api}/project//resource//translation/`, + translationStrings: `${api}/project//resource//translation//strings`, + translationString: `${api}/project//resource//translation//string/`, + } +} diff --git a/lib/txProject.js b/src/lib/txProject.js similarity index 100% rename from lib/txProject.js rename to src/lib/txProject.js diff --git a/lib/txResource.js b/src/lib/txResource.js similarity index 100% rename from lib/txResource.js rename to src/lib/txResource.js diff --git a/lib/zdArticles.js b/src/lib/zdArticles.js similarity index 100% rename from lib/zdArticles.js rename to src/lib/zdArticles.js diff --git a/lib/zdCategories.js b/src/lib/zdCategories.js similarity index 100% rename from lib/zdCategories.js rename to src/lib/zdCategories.js diff --git a/lib/zdSections.js b/src/lib/zdSections.js similarity index 100% rename from lib/zdSections.js rename to src/lib/zdSections.js diff --git a/lib/zdTranslations.js b/src/lib/zdTranslations.js similarity index 100% rename from lib/zdTranslations.js rename to src/lib/zdTranslations.js diff --git a/templates/error_page.hdbs b/src/templates/error_page.hdbs similarity index 100% rename from templates/error_page.hdbs rename to src/templates/error_page.hdbs diff --git a/templates/layout.hdbs b/src/templates/layout.hdbs similarity index 100% rename from templates/layout.hdbs rename to src/templates/layout.hdbs diff --git a/templates/loading_page.hdbs b/src/templates/loading_page.hdbs similarity index 100% rename from templates/loading_page.hdbs rename to src/templates/loading_page.hdbs diff --git a/templates/sync_page.hdbs b/src/templates/sync_page.hdbs similarity index 100% rename from templates/sync_page.hdbs rename to src/templates/sync_page.hdbs diff --git a/test/data/article.json b/test-depd/data/article.json similarity index 100% rename from test/data/article.json rename to test-depd/data/article.json diff --git a/test/data/articleBody.json b/test-depd/data/articleBody.json similarity index 100% rename from test/data/articleBody.json rename to test-depd/data/articleBody.json diff --git a/test/data/articles.json b/test-depd/data/articles.json similarity index 100% rename from test/data/articles.json rename to test-depd/data/articles.json diff --git a/test/data/articlesTranslations.json b/test-depd/data/articlesTranslations.json similarity index 100% rename from test/data/articlesTranslations.json rename to test-depd/data/articlesTranslations.json diff --git a/test/data/resourceStats.json b/test-depd/data/resourceStats.json similarity index 100% rename from test/data/resourceStats.json rename to test-depd/data/resourceStats.json diff --git a/test/data/syncArticleArray.json b/test-depd/data/syncArticleArray.json similarity index 100% rename from test/data/syncArticleArray.json rename to test-depd/data/syncArticleArray.json diff --git a/test/data/syncCategoryArray.json b/test-depd/data/syncCategoryArray.json similarity index 100% rename from test/data/syncCategoryArray.json rename to test-depd/data/syncCategoryArray.json diff --git a/test/data/syncPageCategoryData.json b/test-depd/data/syncPageCategoryData.json similarity index 100% rename from test/data/syncPageCategoryData.json rename to test-depd/data/syncPageCategoryData.json diff --git a/test/data/syncPageDataSet.json b/test-depd/data/syncPageDataSet.json similarity index 100% rename from test/data/syncPageDataSet.json rename to test-depd/data/syncPageDataSet.json diff --git a/test/data/syncPagePagination.json b/test-depd/data/syncPagePagination.json similarity index 100% rename from test/data/syncPagePagination.json rename to test-depd/data/syncPagePagination.json diff --git a/test/data/syncPageSectionData.json b/test-depd/data/syncPageSectionData.json similarity index 100% rename from test/data/syncPageSectionData.json rename to test-depd/data/syncPageSectionData.json diff --git a/test/data/syncSectionArray.json b/test-depd/data/syncSectionArray.json similarity index 100% rename from test/data/syncSectionArray.json rename to test-depd/data/syncSectionArray.json diff --git a/test/data/txProject.json b/test-depd/data/txProject.json similarity index 100% rename from test/data/txProject.json rename to test-depd/data/txProject.json diff --git a/test/data/txRequest.json b/test-depd/data/txRequest.json similarity index 100% rename from test/data/txRequest.json rename to test-depd/data/txRequest.json diff --git a/test/data/txRequestArray.json b/test-depd/data/txRequestArray.json similarity index 100% rename from test/data/txRequestArray.json rename to test-depd/data/txRequestArray.json diff --git a/test/data/txRequestCategory.json b/test-depd/data/txRequestCategory.json similarity index 100% rename from test/data/txRequestCategory.json rename to test-depd/data/txRequestCategory.json diff --git a/test/data/txRequestSection.json b/test-depd/data/txRequestSection.json similarity index 100% rename from test/data/txRequestSection.json rename to test-depd/data/txRequestSection.json diff --git a/test/data/txResource.json b/test-depd/data/txResource.json similarity index 100% rename from test/data/txResource.json rename to test-depd/data/txResource.json diff --git a/test/data/zdCategories.json b/test-depd/data/zdCategories.json similarity index 100% rename from test/data/zdCategories.json rename to test-depd/data/zdCategories.json diff --git a/test/data/zdCategory.json b/test-depd/data/zdCategory.json similarity index 100% rename from test/data/zdCategory.json rename to test-depd/data/zdCategory.json diff --git a/test/data/zdSection.json b/test-depd/data/zdSection.json similarity index 100% rename from test/data/zdSection.json rename to test-depd/data/zdSection.json diff --git a/test/data/zdSections.json b/test-depd/data/zdSections.json similarity index 100% rename from test/data/zdSections.json rename to test-depd/data/zdSections.json diff --git a/test/data/zdTranslations.json b/test-depd/data/zdTranslations.json similarity index 100% rename from test/data/zdTranslations.json rename to test-depd/data/zdTranslations.json diff --git a/test/schemas/translations.json b/test-depd/schemas/translations.json similarity index 100% rename from test/schemas/translations.json rename to test-depd/schemas/translations.json diff --git a/test/testApp.js b/test-depd/testApp.js similarity index 100% rename from test/testApp.js rename to test-depd/testApp.js diff --git a/test/testBacon.js b/test-depd/testBacon.js similarity index 100% rename from test/testBacon.js rename to test-depd/testBacon.js diff --git a/test/txResource.json b/test-depd/txResource.json similarity index 100% rename from test/txResource.json rename to test-depd/txResource.json diff --git a/test/txResourceRequest.json b/test-depd/txResourceRequest.json similarity index 100% rename from test/txResourceRequest.json rename to test-depd/txResourceRequest.json diff --git a/test/zdArticle.html b/test-depd/zdArticle.html similarity index 100% rename from test/zdArticle.html rename to test-depd/zdArticle.html diff --git a/test/zdArticle.json b/test-depd/zdArticle.json similarity index 100% rename from test/zdArticle.json rename to test-depd/zdArticle.json diff --git a/test/.eslintrc b/test/.eslintrc new file mode 100644 index 0000000..09e0892 --- /dev/null +++ b/test/.eslintrc @@ -0,0 +1,17 @@ +{ + "extends": "./setup/.globals.json", + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module" + }, + "rules": { + "strict": 0, + "quotes": [2, "single"], + "no-unused-expressions": 0 + }, + "env": { + "browser": true, + "node": true, + "mocha": true + } +} diff --git a/test/runner.html b/test/runner.html new file mode 100644 index 0000000..371619b --- /dev/null +++ b/test/runner.html @@ -0,0 +1,28 @@ + + + + + + Tests + + + + + + + + + + + + + + + + + + + +
+ + diff --git a/test/setup/.globals.json b/test/setup/.globals.json new file mode 100644 index 0000000..437436b --- /dev/null +++ b/test/setup/.globals.json @@ -0,0 +1,12 @@ +{ + "globals": { + "expect": true, + "mock": true, + "sandbox": true, + "spy": true, + "stub": true, + "useFakeServer": true, + "useFakeTimers": true, + "useFakeXMLHttpRequest": true + } +} diff --git a/test/setup/browser.js b/test/setup/browser.js new file mode 100644 index 0000000..4d55281 --- /dev/null +++ b/test/setup/browser.js @@ -0,0 +1,9 @@ +var mochaGlobals = require('./.globals.json').globals; + +window.mocha.setup('bdd'); +window.onload = function() { + window.mocha.checkLeaks(); + window.mocha.globals(Object.keys(mochaGlobals)); + window.mocha.run(); + require('./setup')(window); +}; diff --git a/test/setup/node.js b/test/setup/node.js new file mode 100644 index 0000000..12d768d --- /dev/null +++ b/test/setup/node.js @@ -0,0 +1,19 @@ +global.chai = require('chai'); +global.sinon = require('sinon'); +global.chai.use(require('sinon-chai')); + +require('babel-core/register'); +require('./setup')(); + +/* + Uncomment the following if your library uses features of the DOM, + for example if writing a jQuery extension, and + add 'simple-jsdom' to the `devDependencies` of your package.json + + Note that JSDom doesn't implement the entire DOM API. If you're using + more advanced or experimental features, you may need to switch to + PhantomJS. Setting that up is currently outside of the scope of this + boilerplate. +*/ +// import simpleJSDom from 'simple-jsdom'; +// simpleJSDom.install(); diff --git a/test/setup/setup.js b/test/setup/setup.js new file mode 100644 index 0000000..413039e --- /dev/null +++ b/test/setup/setup.js @@ -0,0 +1,22 @@ +module.exports = function(root) { + root = root ? root : global; + root.expect = root.chai.expect; + + beforeEach(function() { + // Using these globally-available Sinon features is preferrable, as they're + // automatically restored for you in the subsequent `afterEach` + root.sandbox = root.sinon.sandbox.create(); + root.stub = root.sandbox.stub.bind(root.sandbox); + root.spy = root.sandbox.spy.bind(root.sandbox); + root.mock = root.sandbox.mock.bind(root.sandbox); + root.useFakeTimers = root.sandbox.useFakeTimers.bind(root.sandbox); + root.useFakeXMLHttpRequest = root.sandbox.useFakeXMLHttpRequest.bind(root.sandbox); + root.useFakeServer = root.sandbox.useFakeServer.bind(root.sandbox); + }); + + afterEach(function() { + delete root.stub; + delete root.spy; + root.sandbox.restore(); + }); +}; diff --git a/test/unit/00.api.spec.js b/test/unit/00.api.spec.js new file mode 100755 index 0000000..8c800b9 --- /dev/null +++ b/test/unit/00.api.spec.js @@ -0,0 +1,15 @@ +import TransifexApi from '../../src/lib/transifex-api/transifex-api.js'; + +global.txApi = TransifexApi.connect({ + username: 'alexapi1', + password: 'alexapi1', + base_url: 'http://tx.loc:8000/', +}); +global.slug = 'project' + require('shortid').generate(); + +console.log(slug); +describe('An api class', () => { + it('should exist ', () => { + expect(txApi).to.not.be.undefined; + }); +}); diff --git a/test/unit/js-barebones.js b/test/unit/js-barebones.js new file mode 100644 index 0000000..37e809f --- /dev/null +++ b/test/unit/js-barebones.js @@ -0,0 +1,18 @@ +import main from '../../src/custard-moon'; + +describe('main', () => { + describe('Greet function', () => { + beforeEach(() => { + spy(main, 'greet'); + main.greet(); + }); + + it('should have been run once', () => { + expect(main.greet).to.have.been.calledOnce; + }); + + it('should have always returned hello', () => { + expect(main.greet).to.have.always.returned('hello'); + }); + }); +}); diff --git a/webpack.config.js b/webpack.config.js deleted file mode 100644 index f92af17..0000000 --- a/webpack.config.js +++ /dev/null @@ -1,13 +0,0 @@ - var path = require('path'); - - module.exports = { - entry: './src/app.js', - output: { - path: './dist', - filename: 'app.bundle.js' - }, - resolve: { - root: path.resolve('./lib'), - extensions: ['', '.js'] - } - }; \ No newline at end of file