Comparing version 0.1.3 to 0.2.0
@@ -6,4 +6,7 @@ export = Loadmill; | ||
} | ||
interface TestResult { | ||
interface TestDef { | ||
id: string; | ||
type: string; | ||
} | ||
interface TestResult extends TestDef { | ||
url: string; | ||
@@ -20,4 +23,5 @@ passed: boolean; | ||
run(config: Loadmill.Configuration, paramsOrCallback?: Loadmill.ParamsOrCallback, callback?: Loadmill.Callback): Promise<string>; | ||
wait(loadTestId: string, callback?: Loadmill.Callback): Promise<Loadmill.TestResult>; | ||
wait(testDefOrId: string | Loadmill.TestDef, callback?: Loadmill.Callback): Promise<Loadmill.TestResult>; | ||
runFunctional(config: Loadmill.Configuration, paramsOrCallback?: Loadmill.ParamsOrCallback, callback?: Loadmill.Callback): Promise<Loadmill.TestResult>; | ||
runAsyncFunctional(config: Loadmill.Configuration, paramsOrCallback?: Loadmill.ParamsOrCallback, callback?: Loadmill.Callback): Promise<Loadmill.TestResult>; | ||
}; |
127
lib/index.js
@@ -5,4 +5,40 @@ "use strict"; | ||
var superagent = require("superagent"); | ||
var TYPE_LOAD = 'load'; | ||
var TYPE_FUNCTIONAL = 'functional'; | ||
function Loadmill(_a) { | ||
var token = _a.token; | ||
function _runFunctional(async, config, paramsOrCallback, callback) { | ||
return tslib_1.__awaiter(this, void 0, void 0, function () { | ||
var _this = this; | ||
return tslib_1.__generator(this, function (_a) { | ||
return [2 /*return*/, wrap(function () { return tslib_1.__awaiter(_this, void 0, void 0, function () { | ||
var _a, id, trialResult, incompleteMessage; | ||
return tslib_1.__generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
config = toConfig(config, paramsOrCallback); | ||
config['async'] = async; | ||
return [4 /*yield*/, superagent.post("https://www.loadmill.com/api/tests/trials") | ||
.send(config) | ||
.auth(token, '')]; | ||
case 1: | ||
_a = (_b.sent()).body, id = _a.id, trialResult = _a.trialResult, incompleteMessage = _a.incompleteMessage; | ||
if (incompleteMessage) { | ||
throw Error(incompleteMessage); | ||
} | ||
else { | ||
return [2 /*return*/, { | ||
id: id, | ||
type: TYPE_FUNCTIONAL, | ||
url: "https://www.loadmill.com/app/functional/" + id, | ||
passed: async ? null : isFunctionalPassed(trialResult) | ||
}]; | ||
} | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); }, callback || paramsOrCallback)]; | ||
}); | ||
}); | ||
} | ||
return { | ||
@@ -31,22 +67,24 @@ run: function (config, paramsOrCallback, callback) { | ||
}, | ||
wait: function (loadTestId, callback) { | ||
wait: function (testDefOrId, callback) { | ||
var _this = this; | ||
var _resolve, _reject; | ||
var resolve, reject; | ||
var testDef = typeof testDefOrId === 'string' ? { | ||
id: testDefOrId, | ||
type: TYPE_LOAD | ||
} : testDefOrId; | ||
var testUrls = getTestUrls(testDef); | ||
var intervalId = setInterval(function () { return tslib_1.__awaiter(_this, void 0, void 0, function () { | ||
var result, testResult, err_1; | ||
return tslib_1.__generator(this, function (_a) { | ||
switch (_a.label) { | ||
var _a, trialResult, result, testResult, err_1; | ||
return tslib_1.__generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
_a.trys.push([0, 2, , 3]); | ||
return [4 /*yield*/, superagent.get("https://www.loadmill.com/api/tests/" + loadTestId) | ||
_b.trys.push([0, 2, , 3]); | ||
return [4 /*yield*/, superagent.get(testUrls.api) | ||
.auth(token, '')]; | ||
case 1: | ||
result = (_a.sent()).body.result; | ||
if (result) { | ||
_a = (_b.sent()).body, trialResult = _a.trialResult, result = _a.result; | ||
if (result || trialResult) { | ||
clearInterval(intervalId); | ||
testResult = { | ||
id: loadTestId, | ||
passed: result === 'done', | ||
url: "https://www.loadmill.com/app/test/" + loadTestId | ||
}; | ||
testResult = tslib_1.__assign({}, testDef, { url: testUrls.web, passed: testDef.type === TYPE_LOAD ? | ||
result === 'done' : isFunctionalPassed(trialResult) }); | ||
if (callback) { | ||
@@ -56,3 +94,3 @@ callback(null, testResult); | ||
else { | ||
_resolve(testResult); | ||
resolve(testResult); | ||
} | ||
@@ -62,3 +100,8 @@ } | ||
case 2: | ||
err_1 = _a.sent(); | ||
err_1 = _b.sent(); | ||
if (testDef.type === TYPE_FUNCTIONAL && err_1.status === 404) { | ||
// 404 for functional could be fine when async - keep going: | ||
return [2 /*return*/]; | ||
} | ||
clearInterval(intervalId); | ||
if (callback) { | ||
@@ -68,3 +111,3 @@ callback(err_1, null); | ||
else { | ||
_reject(err_1); | ||
reject(err_1); | ||
} | ||
@@ -76,37 +119,29 @@ return [3 /*break*/, 3]; | ||
}); }, 10 * 1000); | ||
return callback ? null : new Promise(function (resolve, reject) { | ||
_resolve = resolve; | ||
_reject = reject; | ||
return callback ? null : new Promise(function (_resolve, _reject) { | ||
resolve = _resolve; | ||
reject = _reject; | ||
}); | ||
}, | ||
runFunctional: function (config, paramsOrCallback, callback) { | ||
var _this = this; | ||
return wrap(function () { return tslib_1.__awaiter(_this, void 0, void 0, function () { | ||
var _a, id, trialResult, incompleteMessage; | ||
return tslib_1.__generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
config = toConfig(config, paramsOrCallback); | ||
return [4 /*yield*/, superagent.post("https://www.loadmill.com/api/tests/trials") | ||
.send(config) | ||
.auth(token, '')]; | ||
case 1: | ||
_a = (_b.sent()).body, id = _a.id, trialResult = _a.trialResult, incompleteMessage = _a.incompleteMessage; | ||
if (incompleteMessage) { | ||
throw Error(incompleteMessage); | ||
} | ||
else { | ||
return [2 /*return*/, { | ||
id: id, | ||
url: "https://www.loadmill.com/app/functional/" + id, | ||
passed: trialResult && Object.keys(trialResult.failures || {}).length === 0 | ||
}]; | ||
} | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); }, callback || paramsOrCallback); | ||
return _runFunctional(false, config, paramsOrCallback, callback); | ||
}, | ||
runAsyncFunctional: function (config, paramsOrCallback, callback) { | ||
return _runFunctional(true, config, paramsOrCallback, callback); | ||
} | ||
}; | ||
} | ||
function isFunctionalPassed(trialResult) { | ||
return !!trialResult && Object.keys(trialResult.failures || {}).length === 0; | ||
} | ||
function getTestUrls(testDef) { | ||
return { | ||
api: getTestUrl(testDef, 'https://www.loadmill.com/api/tests/', 'trials/', ''), | ||
web: getTestUrl(testDef, 'https://www.loadmill.com/app/', 'functional/', 'test/') | ||
}; | ||
} | ||
function getTestUrl(_a, prefix, funcSuffix, loadSuffix) { | ||
var id = _a.id, type = _a.type; | ||
var suffix = type === TYPE_FUNCTIONAL ? funcSuffix : loadSuffix; | ||
return "" + prefix + suffix + id; | ||
} | ||
function wrap(asyncFunction, paramsOrCallback) { | ||
@@ -113,0 +148,0 @@ var promise = asyncFunction(); |
@@ -13,3 +13,6 @@ "use strict"; | ||
.option("-l, --load-test", "Launch a load test. If not set, a functional test will run instead.") | ||
.option("-w, --wait", "Wait for the load test to finish. Functional tests are always waited on.") | ||
.option("-a, --async", "Run the test asynchronously - affects only functional tests. " + | ||
"Use this if your test can take longer than 25 seconds (otherwise it will timeout).") | ||
.option("-w, --wait", "Wait for the test to finish. Functional tests are automatically waited on " + | ||
"unless async flag is turned on.") | ||
.option("-n, --no-bail", "Return exit code 0 even if test fails.") | ||
@@ -25,7 +28,7 @@ .option("-q, --quiet", "Do not print out anything (except errors).") | ||
return tslib_1.__awaiter(this, void 0, void 0, function () { | ||
var wait, bail, quiet, token, verbose, loadTest, _a, file, rawParams, parameters, res, id, loadmill; | ||
var wait, bail, async, quiet, token, verbose, loadTest, _a, file, rawParams, parameters, res, id, loadmill, method; | ||
return tslib_1.__generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
wait = program.wait, bail = program.bail, quiet = program.quiet, token = program.token, verbose = program.verbose, loadTest = program.loadTest, _a = program.args, file = _a[0], rawParams = _a.slice(1); | ||
wait = program.wait, bail = program.bail, async = program.async, quiet = program.quiet, token = program.token, verbose = program.verbose, loadTest = program.loadTest, _a = program.args, file = _a[0], rawParams = _a.slice(1); | ||
if (!file) { | ||
@@ -45,2 +48,3 @@ validationFailed("No configuration file provided."); | ||
bail: bail, | ||
async: async, | ||
quiet: quiet, | ||
@@ -54,3 +58,3 @@ token: token, | ||
loadmill = Loadmill({ token: token }); | ||
if (!loadTest) return [3 /*break*/, 4]; | ||
if (!loadTest) return [3 /*break*/, 2]; | ||
if (verbose) { | ||
@@ -62,16 +66,18 @@ console.log("Launching load test..."); | ||
id = _b.sent(); | ||
if (!wait) return [3 /*break*/, 3]; | ||
return [3 /*break*/, 4]; | ||
case 2: | ||
if (verbose) { | ||
console.log("Waiting for load test:", id); | ||
console.log("Running functional test..."); | ||
} | ||
return [4 /*yield*/, loadmill.wait(id)]; | ||
case 2: | ||
method = async ? 'runAsyncFunctional' : 'runFunctional'; | ||
return [4 /*yield*/, loadmill[method](file, parameters)]; | ||
case 3: | ||
res = _b.sent(); | ||
_b.label = 3; | ||
case 3: return [3 /*break*/, 6]; | ||
_b.label = 4; | ||
case 4: | ||
if (!(wait && (loadTest || async))) return [3 /*break*/, 6]; | ||
if (verbose) { | ||
console.log("Running functional test..."); | ||
console.log("Waiting for test:", res ? res.id : id); | ||
} | ||
return [4 /*yield*/, loadmill.runFunctional(file, parameters)]; | ||
return [4 /*yield*/, loadmill.wait(res || id)]; | ||
case 5: | ||
@@ -84,3 +90,3 @@ res = _b.sent(); | ||
} | ||
if (res && !res.passed) { | ||
if (res && res.passed != null && !res.passed) { | ||
console.error("Test failed."); | ||
@@ -87,0 +93,0 @@ if (bail) { |
{ | ||
"name": "loadmill", | ||
"version": "0.1.3", | ||
"version": "0.2.0", | ||
"description": "A node.js module for running load tests and functional tests on loadmill.com", | ||
@@ -24,10 +24,10 @@ "keywords": [ | ||
"dependencies": { | ||
"commander": "^2.11.0", | ||
"superagent": "^3.5.2", | ||
"tslib": "^1.7.1" | ||
"commander": "^2.15.0", | ||
"superagent": "^3.8.2", | ||
"tslib": "^1.9.0" | ||
}, | ||
"devDependencies": { | ||
"@types/node": "^8.0.10", | ||
"typescript": "^2.4.1" | ||
"@types/node": "^9.4.7", | ||
"typescript": "^2.7.2" | ||
} | ||
} |
@@ -63,3 +63,3 @@ # Loadmill | ||
.then(loadmill.wait) | ||
// -> {id: string, passed: boolean, url: string} | ||
// -> {id: string, type: 'load', passed: boolean, url: string} | ||
.then(result => console.log(result)); | ||
@@ -74,6 +74,22 @@ ``` | ||
loadmill.runFunctional("./load-tests/api_test.json") | ||
// -> {id: string, passed: boolean, url: string} | ||
// -> {id: string, type: 'functional', passed: boolean, url: string} | ||
.then(result => console.log(result)); | ||
``` | ||
If your functional test is supposed to, or may, take longer than 25 seconds, you can use `runAsyncFunctional` instead: | ||
```js | ||
loadmill.runAsyncFunctional("./load-tests/api_test.json") | ||
// -> {id: string, type: 'functional', passed: null, url: string} | ||
.then(result => console.log(result)); | ||
``` | ||
Note that in this case the `passed` property is `null` since the promise resolves before the test is finished. | ||
If you want to wait for the full result you can use `wait` here as well: | ||
```js | ||
loadmill.runAsyncFunctional("./load-tests/api_test.json") | ||
.then(loadmill.wait) | ||
// -> {id: string, type: 'functional', passed: boolean, url: string} | ||
.then(result => console.log(result)); | ||
``` | ||
### Parameters | ||
@@ -141,5 +157,6 @@ | ||
- `-l, --load-test` Launch a load test. If not set, a functional test will run instead. | ||
- `-w, --wait` Wait for the load test to finish. Functional tests are always waited on. | ||
- `-a, --async` Run the test asynchronously - affects only functional tests. Use this if your test can take longer than 25 seconds (otherwise it will timeout). | ||
- `-w, --wait` Wait for the test to finish. Functional tests are automatically waited on unless async flag is turned on. | ||
- `-n, --no-bail` Return exit code 0 even if test fails. | ||
- `-q, --quiet` Do not print out anything (except errors). | ||
- `-v, --verbose` Print out extra information for debugging (trumps `-q`). |
135
src/index.ts
@@ -11,4 +11,8 @@ import * as fs from 'fs'; | ||
export interface TestResult { | ||
export interface TestDef { | ||
id: string; | ||
type: string; | ||
} | ||
export interface TestResult extends TestDef { | ||
url: string; | ||
@@ -23,3 +27,45 @@ passed: boolean; | ||
const TYPE_LOAD = 'load'; | ||
const TYPE_FUNCTIONAL = 'functional'; | ||
function Loadmill({token}: Loadmill.LoadmillOptions) { | ||
async function _runFunctional( | ||
async: boolean, | ||
config: Loadmill.Configuration, | ||
paramsOrCallback: Loadmill.ParamsOrCallback, | ||
callback: Loadmill.Callback) { | ||
return wrap( | ||
async () => { | ||
config = toConfig(config, paramsOrCallback); | ||
config['async'] = async; | ||
const { | ||
body: { | ||
id, | ||
trialResult, | ||
incompleteMessage, | ||
} | ||
} = await superagent.post("https://www.loadmill.com/api/tests/trials") | ||
.send(config) | ||
.auth(token, ''); | ||
if (incompleteMessage) { | ||
throw Error(incompleteMessage); | ||
} | ||
else { | ||
return { | ||
id, | ||
type: TYPE_FUNCTIONAL, | ||
url: `https://www.loadmill.com/app/functional/${id}`, | ||
passed: async ? null : isFunctionalPassed(trialResult), | ||
}; | ||
} | ||
}, | ||
callback || paramsOrCallback | ||
); | ||
} | ||
return { | ||
@@ -48,17 +94,25 @@ run( | ||
wait(loadTestId: string, callback?: Loadmill.Callback): Promise<Loadmill.TestResult> { | ||
let _resolve, _reject; | ||
wait(testDefOrId: string | Loadmill.TestDef, callback?: Loadmill.Callback): Promise<Loadmill.TestResult> { | ||
let resolve, reject; | ||
const testDef = typeof testDefOrId === 'string' ? { | ||
id: testDefOrId, | ||
type: TYPE_LOAD, | ||
} : testDefOrId; | ||
const testUrls = getTestUrls(testDef); | ||
const intervalId = setInterval(async () => { | ||
try { | ||
const {body: {result}} = await superagent.get(`https://www.loadmill.com/api/tests/${loadTestId}`) | ||
const {body: {trialResult, result}} = await superagent.get(testUrls.api) | ||
.auth(token, ''); | ||
if (result) { | ||
if (result || trialResult) { | ||
clearInterval(intervalId); | ||
const testResult = { | ||
id: loadTestId, | ||
passed: result === 'done', | ||
url: `https://www.loadmill.com/app/test/${loadTestId}`, | ||
...testDef, | ||
url: testUrls.web, | ||
passed: testDef.type === TYPE_LOAD ? | ||
result === 'done' : isFunctionalPassed(trialResult), | ||
}; | ||
@@ -70,3 +124,3 @@ | ||
else { | ||
_resolve(testResult); | ||
resolve(testResult); | ||
} | ||
@@ -76,2 +130,9 @@ } | ||
catch (err) { | ||
if (testDef.type === TYPE_FUNCTIONAL && err.status === 404) { | ||
// 404 for functional could be fine when async - keep going: | ||
return; | ||
} | ||
clearInterval(intervalId); | ||
if (callback) { | ||
@@ -81,3 +142,3 @@ callback(err, null); | ||
else { | ||
_reject(err); | ||
reject(err); | ||
} | ||
@@ -88,5 +149,5 @@ } | ||
return callback ? null! as Promise<any> : new Promise((resolve, reject) => { | ||
_resolve = resolve; | ||
_reject = reject; | ||
return callback ? null! as Promise<any> : new Promise((_resolve, _reject) => { | ||
resolve = _resolve; | ||
reject = _reject; | ||
}); | ||
@@ -100,29 +161,11 @@ }, | ||
return wrap( | ||
async () => { | ||
config = toConfig(config, paramsOrCallback); | ||
return _runFunctional(false, config, paramsOrCallback, callback); | ||
}, | ||
const { | ||
body: { | ||
id, | ||
trialResult, | ||
incompleteMessage, | ||
} | ||
} = await superagent.post("https://www.loadmill.com/api/tests/trials") | ||
.send(config) | ||
.auth(token, ''); | ||
runAsyncFunctional( | ||
config: Loadmill.Configuration, | ||
paramsOrCallback?: Loadmill.ParamsOrCallback, | ||
callback?: Loadmill.Callback): Promise<Loadmill.TestResult> { | ||
if (incompleteMessage) { | ||
throw Error(incompleteMessage); | ||
} | ||
else { | ||
return { | ||
id, | ||
url: `https://www.loadmill.com/app/functional/${id}`, | ||
passed: trialResult && Object.keys(trialResult.failures || {}).length === 0, | ||
}; | ||
} | ||
}, | ||
callback || paramsOrCallback | ||
); | ||
return _runFunctional(true, config, paramsOrCallback, callback); | ||
}, | ||
@@ -132,2 +175,18 @@ }; | ||
function isFunctionalPassed(trialResult) { | ||
return !!trialResult && Object.keys(trialResult.failures || {}).length === 0; | ||
} | ||
function getTestUrls(testDef: Loadmill.TestDef) { | ||
return { | ||
api: getTestUrl(testDef, 'https://www.loadmill.com/api/tests/', 'trials/', ''), | ||
web: getTestUrl(testDef, 'https://www.loadmill.com/app/', 'functional/', 'test/'), | ||
}; | ||
} | ||
function getTestUrl({id, type}: Loadmill.TestDef, prefix: string, funcSuffix: string, loadSuffix: string) { | ||
const suffix = type === TYPE_FUNCTIONAL ? funcSuffix : loadSuffix; | ||
return `${prefix}${suffix}${id}` | ||
} | ||
function wrap(asyncFunction, paramsOrCallback?: Loadmill.ParamsOrCallback) { | ||
@@ -134,0 +193,0 @@ const promise = asyncFunction(); |
@@ -13,3 +13,6 @@ import * as Loadmill from './index'; | ||
.option("-l, --load-test", "Launch a load test. If not set, a functional test will run instead.") | ||
.option("-w, --wait", "Wait for the load test to finish. Functional tests are always waited on.") | ||
.option("-a, --async", "Run the test asynchronously - affects only functional tests. " + | ||
"Use this if your test can take longer than 25 seconds (otherwise it will timeout).") | ||
.option("-w, --wait", "Wait for the test to finish. Functional tests are automatically waited on " + | ||
"unless async flag is turned on.") | ||
.option("-n, --no-bail", "Return exit code 0 even if test fails.") | ||
@@ -31,2 +34,3 @@ .option("-q, --quiet", "Do not print out anything (except errors).") | ||
bail, | ||
async, | ||
quiet, | ||
@@ -57,2 +61,3 @@ token, | ||
bail, | ||
async, | ||
quiet, | ||
@@ -74,9 +79,2 @@ token, | ||
id = await loadmill.run(file, parameters); | ||
if (wait) { | ||
if (verbose) { | ||
console.log("Waiting for load test:", id); | ||
} | ||
res = await loadmill.wait(id); | ||
} | ||
} | ||
@@ -87,5 +85,14 @@ else { | ||
} | ||
res = await loadmill.runFunctional(file, parameters); | ||
const method = async ? 'runAsyncFunctional' : 'runFunctional'; | ||
res = await loadmill[method](file, parameters); | ||
} | ||
if (wait && (loadTest || async)) { | ||
if (verbose) { | ||
console.log("Waiting for test:", res ? res.id : id); | ||
} | ||
res = await loadmill.wait(res || id); | ||
} | ||
if (!quiet) { | ||
@@ -95,3 +102,3 @@ console.log(res || id); | ||
if (res && !res.passed) { | ||
if (res && res.passed != null && !res.passed) { | ||
console.error("Test failed."); | ||
@@ -98,0 +105,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
37712
612
160
11
Updatedcommander@^2.15.0
Updatedsuperagent@^3.8.2
Updatedtslib@^1.9.0