testable-utils
Advanced tools
Comparing version 0.2.6 to 0.2.9
@@ -1,11 +0,11 @@ | ||
var _ = require('lodash'); | ||
var Baby = require('babyparse'); | ||
var request = require('request'); | ||
var util = require('util'); | ||
var fs = require('fs'); | ||
const _ = require('lodash'); | ||
const Baby = require('babyparse'); | ||
const request = require('request'); | ||
const util = require('util'); | ||
const fs = require('fs'); | ||
var AgentKey = process.env.TESTABLE_KEY; | ||
var BaseUrl = process.env.TESTABLE_BASE_URL; | ||
const AgentKey = process.env.TESTABLE_KEY; | ||
const BaseUrl = process.env.TESTABLE_BASE_URL; | ||
var DataTable = function(executionId, name, info, log) { | ||
const DataTable = function(executionId, name, info, log) { | ||
this.executionId = executionId; | ||
@@ -66,7 +66,7 @@ this.name = name; | ||
self.log.error('Problem retrieving data table ' + name + ': ' + error.message); | ||
reject(error); | ||
throw new Error(error); | ||
} else if (response.statusCode !== 200) { | ||
msg = 'Problem retrieving rows at ' + path + '. Status code ' + response.statusCode; | ||
self.log.error(msg); | ||
reject(msg); | ||
throw new Error(msg); | ||
} else if (body) { | ||
@@ -85,3 +85,3 @@ var indices = JSON.parse(body); | ||
self.log.error(msg); | ||
reject(msg); | ||
throw new Error(msg); | ||
} | ||
@@ -88,0 +88,0 @@ }); |
@@ -46,2 +46,3 @@ | ||
const isSmokeTest = Number(process.env.TESTABLE_CHUNK_ID) < 0; | ||
var isLocal; | ||
@@ -64,5 +65,6 @@ var info; | ||
const doNotWait = isSmokeTest || isLocal; | ||
const writeStream = isLocal ? null : fs.createWriteStream(process.env.TESTABLE_RESULT_FILE, { flags: 'a' }); | ||
const log = createLog(writeStream); | ||
const results = createResults(writeStream); | ||
const results = createResults(writeStream, doNotWait); | ||
results.current = results('dummy','http://dummy.com'); | ||
@@ -110,10 +112,10 @@ results.toResourceName = function(url) { return url; }; | ||
wdio.registerCsvCommands(browser, csv); | ||
wdio.registerResultsCommands(browser, results); | ||
wdio.registerResultsCommands(browser, results, doNotWait); | ||
wdio.registerInfoCommands(browser, info); | ||
wdio.registerStopwatchCommands(browser, stopwatch); | ||
wdio.registerEventsCommands(browser, events); | ||
wdio.registerEventsCommands(browser, events, doNotWait); | ||
} | ||
module.exports.isLocal = isLocal; | ||
module.exports.isSmokeTest = Number(process.env.TESTABLE_CHUNK_ID) < 0; | ||
module.exports.isSmokeTest = isSmokeTest; | ||
module.exports.info = info; | ||
@@ -120,0 +122,0 @@ module.exports.log = log; |
@@ -0,1 +1,20 @@ | ||
const request = require('request'); | ||
const _ = require('lodash'); | ||
const AgentKey = process.env.TESTABLE_KEY; | ||
const BaseUrl = process.env.TESTABLE_BASE_URL; | ||
const PollIntervalMs = 1000; | ||
function delay(t, v) { | ||
return new Promise(function(resolve) { | ||
setTimeout(resolve.bind(null, v), t) | ||
}); | ||
} | ||
Promise.prototype.delay = function(t) { | ||
return this.then(function(v) { | ||
return delay(t, v); | ||
}); | ||
} | ||
function toMetricOptions(args, hasKey) { | ||
@@ -23,4 +42,26 @@ if (args.length === 0) { | ||
function createResults(writeStream) { | ||
return function(resource, url) { | ||
function toMetricGetOptions(args) { | ||
if (args.length === 0) { | ||
return null; | ||
} | ||
var options; | ||
if (args[0] !== null && typeof args[0] === 'object') { | ||
options = args[0]; | ||
} else { | ||
options = { name: args[0] }; | ||
if (args[1]) | ||
options.key = args[1]; | ||
} | ||
return options; | ||
} | ||
function toMetricGetPath(options, execId) { | ||
var path = `/executions/${execId}/metric?name=${options.name}`; | ||
if (options.namespace) | ||
path += `&ns=${options.namespace}`; | ||
return path; | ||
} | ||
function createResults(writeStream, noRemote) { | ||
const answer = function(resource, url) { | ||
return { | ||
@@ -60,2 +101,78 @@ data: { | ||
}; | ||
answer.get = function() { | ||
const execId = Number(process.env.TESTABLE_EXECUTION_ID); | ||
if (noRemote || !execId) { | ||
return Promise.resolve(NaN); | ||
} else { | ||
const options = toMetricGetOptions(arguments); | ||
if (options) { | ||
const path = toMetricGetPath(options, execId); | ||
return new Promise(function(resolve, reject) { | ||
var url = BaseUrl + path; | ||
url += (path.indexOf('?') !== -1 ? '&' : '?') + "key=" + AgentKey; | ||
request({ url: url }, function (error, response, body) { | ||
if (error) { | ||
reject(error); | ||
} else if (response.statusCode !== 200) { | ||
reject('Invalid response code ' + response.statusCode); | ||
} else if (body) { | ||
var metric = JSON.parse(body); | ||
if (options.key) | ||
resolve(metric && metric.metricValueMap && metric.metricValueMap[options.key]); | ||
else | ||
resolve(metric && metric.metricValue); | ||
} else { | ||
reject('No response received for metric ' + options.name); | ||
} | ||
}); | ||
}); | ||
} else { | ||
return Promise.reject('Invalid parameters to results.get(name[, key])'); | ||
} | ||
} | ||
}; | ||
function doGetResult(options, start) { | ||
return answer.get(options).then(function(value) { | ||
if (_.isNumber(value) && options.condition(value)) | ||
return Promise.resolve(value); | ||
else if (shouldContinuePolling(options, start)) { | ||
return delay(PollIntervalMs).then(function() { | ||
return doGetResult(options, start); | ||
}); | ||
} else { | ||
return Promise.reject(`Value ${value} for ${options.name} never matched condition specified`); | ||
} | ||
}, function(err) { | ||
if (shouldContinuePolling(options, start)) { | ||
return delay(PollIntervalMs).then(function() { | ||
return doGetResult(options, start); | ||
}); | ||
} else { | ||
return Promise.reject('Failed to get metric value that matches condition. Error: ' + err); | ||
} | ||
}); | ||
} | ||
function shouldContinuePolling(options, start) { | ||
return !_.isNumber(options.timeout) || options.timeout === 0 || (Date.now() + PollIntervalMs - start < options.timeout); | ||
} | ||
answer.waitForCondition = function(options) { | ||
if (noRemote) | ||
return Promise.resolve(); | ||
else if (!_.isFunction(options.condition)) | ||
return Promise.reject('Must specify a condition function'); | ||
else { | ||
const start = Date.now(); | ||
return doGetResult(options, start); | ||
} | ||
}; | ||
answer.waitForValue = function(options) { | ||
options.condition = function(val) { return val >= options.value; }; | ||
return answer.waitForCondition(options); | ||
}; | ||
return answer; | ||
} | ||
@@ -62,0 +179,0 @@ |
@@ -1,2 +0,2 @@ | ||
var _ = require('lodash'); | ||
const _ = require('lodash'); | ||
@@ -41,3 +41,42 @@ function isWdioContext(browser) { | ||
function registerResultsCommands(browser, results) { | ||
function doGetResult(results, options, start) { | ||
return results.get(options).then(function(value) { | ||
if (_.isNumber(value) && options.condition(value)) | ||
return Promise.resolve(value); | ||
else if (shouldContinuePolling(options, start)) { | ||
return delay(PollIntervalMs).then(function() { | ||
return doGetResult(results, options, start); | ||
}); | ||
} else { | ||
return Promise.reject(`Value ${value} for ${options.name} never matched condition specified`); | ||
} | ||
}, function(err) { | ||
if (shouldContinuePolling(options, start)) { | ||
return delay(PollIntervalMs).then(function() { | ||
return doGetResult(results, options, start); | ||
}); | ||
} else { | ||
return Promise.reject('Failed to get metric value that matches condition. Error: ' + err); | ||
} | ||
}); | ||
} | ||
function shouldContinuePolling(options, start) { | ||
return !_.isNumber(options.timeout) || options.timeout === 0 || (Date.now() + PollIntervalMs - start < options.timeout); | ||
} | ||
function waitForCondition(options, results, doNotWait) { | ||
if (doNotWait) | ||
return Promise.resolve(); | ||
else if (!_.isFunction(options.condition)) | ||
return Promise.reject('Must specify a condition function'); | ||
else { | ||
const start = Date.now(); | ||
return doGetResult(results, options, start).catch(function(err) { | ||
throw new Error(err); | ||
}); | ||
} | ||
} | ||
function registerResultsCommands(browser, results, doNotWait) { | ||
if (isWdioContext(browser)) { | ||
@@ -59,2 +98,15 @@ browser.addCommand('testableResult', function(resource, url) { | ||
}); | ||
browser.addCommand('testableGetMetric', function async() { | ||
return results.get.apply(results, arguments); | ||
}); | ||
browser.addCommand('testableWaitForValue', function async(options) { | ||
return results.waitForValue(options).catch(function(err) { | ||
throw new Error(err); | ||
}); | ||
}); | ||
browser.addCommand('testableWaitForCondition', function async(options) { | ||
return results.waitForCondition(options).catch(function(err) { | ||
throw new Error(err); | ||
}); | ||
}); | ||
} | ||
@@ -84,24 +136,28 @@ } | ||
function registerEventsCommands(browser, events, log) { | ||
function registerEventsCommands(browser, events, doNotWait) { | ||
if (isWdioContext(browser)) { | ||
browser.addCommand('testableWaitForEvent', function async(eventName, timeout) { | ||
var resolved = false; | ||
return new Promise(function(resolve, reject) { | ||
var timeoutHandle; | ||
const listener = function(contents) { | ||
resolved = true; | ||
if (timeoutHandle) | ||
clearTimeout(timeoutHandle); | ||
resolve(contents); | ||
}; | ||
if (timeout && timeout > 0) { | ||
timeoutHandle = setTimeout(function() { | ||
if (!resolved) { | ||
events.off(eventName, listener); | ||
reject(`Timeout waiting for event ${eventName}`); | ||
} | ||
}, timeout); | ||
} | ||
events.once(eventName, listener); | ||
}); | ||
browser.addCommand('testableWaitForEvent', function async(eventName, timeout, defaultVal) { | ||
if (doNotWait) | ||
return Promise.resolve(defaultVal); | ||
else { | ||
var resolved = false; | ||
return new Promise(function(resolve, reject) { | ||
var timeoutHandle; | ||
const listener = function(contents) { | ||
resolved = true; | ||
if (timeoutHandle) | ||
clearTimeout(timeoutHandle); | ||
resolve(contents); | ||
}; | ||
if (timeout && timeout > 0) { | ||
timeoutHandle = setTimeout(function() { | ||
if (!resolved) { | ||
events.off(eventName, listener); | ||
reject(`Timeout waiting for event ${eventName}`); | ||
} | ||
}, timeout); | ||
} | ||
events.once(eventName, listener); | ||
}); | ||
} | ||
}); | ||
@@ -108,0 +164,0 @@ } |
{ | ||
"name": "testable-utils", | ||
"version": "0.2.6", | ||
"version": "0.2.9", | ||
"description": "Utilities for Testable scripts", | ||
@@ -5,0 +5,0 @@ "author": "Avi Stramer", |
@@ -12,2 +12,3 @@ # Testable Script Utilities | ||
* [Manual Live Event](#manual-live-event) | ||
* [Coordination Across Virtual Users](#coordination-across-virtual-users) | ||
* [Webdriver.io Custom Commands](#webdriverio-commands) | ||
@@ -88,2 +89,47 @@ * [Screenshots](#screenshots) | ||
##### Get execution wide metric value | ||
```javascript | ||
results.get(name, bucket) | ||
results.get(options) | ||
``` | ||
Read the current value of a metric aggregated across the entire test execution. | ||
For example to read the current value of the ```Slow Requests``` custom counter metric: | ||
```javascript | ||
results.get('Slow Requests').then(function(value) { | ||
// use the value here | ||
}); | ||
results.get({ namespace: 'User', name: 'Slow Requests' }).then(function(value) { | ||
// use the value here | ||
}); | ||
``` | ||
##### Wait for metric value | ||
```javascript | ||
results.waitForValue(options) | ||
``` | ||
Wait for the indicated metric to be greater than or equal to the specified value across the entire test execution or for the timeout to be reached. Returns a Promise object. When run locally or in a smoke test it will resolve successfully immediately. | ||
The options object can include: | ||
* `namespace`: Namespace of the metric. Defaults to `Testable` for system metrics and `User` otherwise. | ||
* `name`: Metric name | ||
* `value`: The value the metric must be greater than or equal to before the Promise resolves successfully. Required. | ||
* `timeout`: Optional timeout (in milliseconds) after which to fail and stop waiting. Defaults to 0 (i.e. no timeout). | ||
* `key`: Bucket to get the metric for. Not applicable for counters but required for histogram and timing metrics | ||
For example to wait on the value of the counter `Slow Requests` to be >= 2: | ||
```javascript | ||
results().counter('Slow Requests', 1, 'requests'); | ||
results.waitForValue({ namespace: 'User', name: 'Slow Requests', value: 2, timeout: 10000 }).then(function() { | ||
// called when the metric reaches at least 2 aggregated across the test execution or immediately when run locally or in a smoke test | ||
}); | ||
``` | ||
### Stopwatch | ||
@@ -262,5 +308,5 @@ | ||
it('should load url', function() { | ||
var url = 'http://google.com'; // default for local or smoke test | ||
if (!fireNow) | ||
url = browser.testableWaitForEvent('go-time'); | ||
browser.testableLogInfo('Waiting on load-url event'); | ||
// no timeout (0), use Google url for local/smoke testing | ||
const url = browser.testableWaitForEvent('load-url', 0, 'https://google.com'); | ||
browser.url(url); | ||
@@ -373,6 +419,10 @@ browser.testableScreenshot('Requested Url'); | ||
<tr> | ||
<td><pre>browser.testableWaitForEvent(eventName[, timeout]);</pre></td> | ||
<td><a href="#manual-live-events"><pre>events.on(eventName);</pre></a></td> | ||
<td><pre>browser.testableWaitForEvent(eventName[, timeout[, defaultVal]]);</pre></td> | ||
<td><a href="#manual-live-event"><pre>events.on(eventName);</pre></a></td> | ||
</tr> | ||
<tr> | ||
<td><pre>browser.testableWaitForValue(options);</pre></td> | ||
<td><a href="#wait-for-metric-value"><pre>results.waitForValue(options);</pre></a></td> | ||
</tr> | ||
<tr> | ||
<td><pre>// blocks until done() is called | ||
@@ -379,0 +429,0 @@ browser.testableStopwatch(function(done) { |
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
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
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
39985
696
436
15