Comparing version 1.0.4 to 1.1.0
@@ -25,2 +25,3 @@ import './polyfills'; | ||
additionalDescription: string; | ||
labels?: string[] | null; | ||
} | ||
@@ -27,0 +28,0 @@ interface TestResult extends TestDef { |
@@ -179,3 +179,3 @@ "use strict"; | ||
var _this = this; | ||
var overrideParameters, suiteId, additionalDescription; | ||
var overrideParameters, suiteId, additionalDescription, labels; | ||
return tslib_1.__generator(this, function (_a) { | ||
@@ -189,2 +189,3 @@ overrideParameters = typeof paramsOrCallback !== 'function' ? paramsOrCallback : {}; | ||
additionalDescription = suite.additionalDescription; | ||
labels = suite.labels; | ||
} | ||
@@ -196,3 +197,3 @@ return [2 /*return*/, wrap(function () { return tslib_1.__awaiter(_this, void 0, void 0, function () { | ||
case 0: return [4 /*yield*/, superagent.post(testingServer + "/api/test-suites/" + suiteId + "/run") | ||
.send({ overrideParameters: overrideParameters, additionalDescription: additionalDescription }) | ||
.send({ overrideParameters: overrideParameters, additionalDescription: additionalDescription, labels: labels }) | ||
.auth(token, '')]; | ||
@@ -199,0 +200,0 @@ case 1: |
@@ -8,3 +8,3 @@ "use strict"; | ||
program | ||
.usage("<config-file-or-folder | testSuiteId> -t <token> [options] [parameter=value...]") | ||
.usage("<load-config-file-or-folder | testSuiteId> -t <token> [options] [parameter=value...]") | ||
.description("Run a load test or a test suite on loadmill.com.\n " + | ||
@@ -14,9 +14,7 @@ "You may set parameter values by passing space-separated 'name=value' pairs, e.g. 'host=www.myapp.com port=80'.\n\n " + | ||
.option("-t, --token <token>", "Loadmill API Token. You must provide a token in order to run tests.") | ||
.option("-l, --load-test", "Launch a load test. If not set, a functional test will run instead.") | ||
.option("-l, --load-test", "Launch a load test.") | ||
.option("-s, --test-suite", "Launch a test suite. If set then a test suite id must be provided instead of config file.") | ||
.option("--additional-description <description>", "Add an aditional description at the end of the current suite's description - available only for test suites.") | ||
.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("--additional-description <description>", "Add an additional description at the end of the current suite's description - available only for test suites.") | ||
.option("--labels <labels>", "Run flows that are assigned to a specific label. Multiple labels can be provided by seperated them with ", " (e.g. 'label1,label2').") | ||
.option("-w, --wait", "Wait for the test to finish.") | ||
.option("-n, --no-bail", "Return exit code 0 even if test fails.") | ||
@@ -26,3 +24,2 @@ .option("-q, --quiet", "Do not print out anything (except errors).") | ||
.option("--colors", "Print test results in color") | ||
.option("-c, --local", "Execute functional test synchronously on local machine. This flag trumps load-test and async options") | ||
.parse(process.argv); | ||
@@ -35,7 +32,7 @@ start()["catch"](function (err) { | ||
return tslib_1.__awaiter(this, void 0, void 0, function () { | ||
var wait, bail, async, quiet, token, verbose, colors, local, loadTest, testSuite, additionalDescription, _a, input, rawParams, logger, parameters, loadmill, res, suite, running, testSuiteRunId, e_1, extInfo, fileOrFolder, listOfFiles, _i, listOfFiles_1, file, res, id, method; | ||
var wait, bail, async, quiet, token, verbose, colors, local, loadTest, testSuite, additionalDescription, labels, _a, input, rawParams, logger, parameters, loadmill, res, suite, running, testSuiteRunId, e_1, extInfo, fileOrFolder, listOfFiles, _i, listOfFiles_1, file, res, id, method; | ||
return tslib_1.__generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
wait = program.wait, bail = program.bail, async = program.async, quiet = program.quiet, token = program.token, verbose = program.verbose, colors = program.colors, local = program.local, loadTest = program.loadTest, testSuite = program.testSuite, additionalDescription = program.additionalDescription, _a = program.args, input = _a[0], rawParams = _a.slice(1); | ||
wait = program.wait, bail = program.bail, async = program.async, quiet = program.quiet, token = program.token, verbose = program.verbose, colors = program.colors, local = program.local, loadTest = program.loadTest, testSuite = program.testSuite, additionalDescription = program.additionalDescription, labels = program.labels, _a = program.args, input = _a[0], rawParams = _a.slice(1); | ||
logger = new utils_1.Logger(verbose, colors); | ||
@@ -60,2 +57,3 @@ if (!token) { | ||
additionalDescription: additionalDescription, | ||
labels: labels, | ||
parameters: parameters | ||
@@ -70,3 +68,3 @@ }); | ||
res = void 0; | ||
suite = { id: input, additionalDescription: additionalDescription }; | ||
suite = { id: input, additionalDescription: additionalDescription, labels: utils_1.convertStrToArr(labels) }; | ||
_b.label = 1; | ||
@@ -73,0 +71,0 @@ case 1: |
@@ -39,2 +39,5 @@ "use strict"; | ||
}; | ||
exports.convertStrToArr = function (strWithCommas) { | ||
return typeof strWithCommas !== "string" ? null : strWithCommas.split(","); | ||
}; | ||
var printRequest = function (trialRes, assertionErrorsPerRequest, testArgs, logger) { | ||
@@ -41,0 +44,0 @@ if (testArgs && testArgs.verbose) { |
{ | ||
"name": "loadmill", | ||
"version": "1.0.4", | ||
"version": "1.1.0", | ||
"description": "A node.js module for running load tests and functional tests on loadmill.com", | ||
@@ -23,3 +23,4 @@ "keywords": [ | ||
"scripts": { | ||
"prepare": "tsc && rm -f lib/loadmill.d.ts lib/utils.d.ts lib/polyfills.d.ts" | ||
"prepare": "tsc && rm -f lib/loadmill.d.ts lib/utils.d.ts lib/polyfills.d.ts", | ||
"test": "mocha" | ||
}, | ||
@@ -47,4 +48,5 @@ "dependencies": { | ||
"@types/node": "^9.4.7", | ||
"typescript": "^2.7.2" | ||
"typescript": "^2.7.2", | ||
"mocha": "^7.0.0" | ||
} | ||
} |
181
README.md
# Loadmill | ||
Users of [Loadmill](https://www.loadmill.com) can use this node module to: | ||
1. Run load tests on loadmill.com. | ||
2. Run functional tests on loadmill.com. | ||
1. Run API tests on loadmill.com. | ||
2. Run load tests on loadmill.com. | ||
3. Do both programmatically or via [CLI](#cli). | ||
@@ -30,2 +30,30 @@ | ||
### API Tokens | ||
In order to use the Loadmill REST API or our node module and CLI, you will need to generate an [API Token](https://docs.loadmill.com/integrations/api-tokens). | ||
### Test Suites | ||
You may launch an existing test suite by supplying the suite id - this is usually useful for testing your API for regressions after every new deployment. | ||
Test suites are launched and not awaiting the results. | ||
```js | ||
const loadmill = require('loadmill')({token: process.env.LOADMILL_API_TOKEN}); | ||
/** | ||
* @returns { id: 'uuid', type: 'test-suite' } | ||
*/ | ||
const result = await loadmill.runTestSuite({id: "test-suite-uuid"}); | ||
``` | ||
You can also extend the suite object: | ||
```js | ||
const result = await loadmill.runTestSuite({ | ||
id: "test-suite-uuid", | ||
additionalDescription: "description to add", //optional | ||
labels: ["label1", "label2"] //optional - run flows that are assigned to specific label/s | ||
} | ||
``` | ||
### Load tests | ||
The following code runs a very simple load test that gets a single page from `www.myapp.com` every second for one minute: | ||
@@ -35,8 +63,5 @@ ```js | ||
// You may also give a path to a valid JSON file instead: | ||
loadmill.run({requests: [{url: "www.myapp.com"}]}, (err, id) => { | ||
if (!err) { | ||
console.log("Load test started: " + id); | ||
} | ||
}); | ||
// You may also give a path to a valid Test Configuration JSON file instead: | ||
const id = await loadmill.run({requests: [{url: "www.myapp.com"}]}); | ||
console.log("Load test started: " + id); | ||
``` | ||
@@ -47,12 +72,5 @@ | ||
The JSON test configuration may be exported from the loadmill test editor or from an old test run. | ||
Read more about the configuration format [here](https://docs.loadmill.com/test-scenarios/configuration-files). | ||
### Using Promises | ||
Read more about the configuration format [here](https://docs.loadmill.com/load-testing/working-with-the-test-editor/configuration-files). | ||
Every function that accepts a callback will return a promise instead if no callback is provided (and vice versa): | ||
```js | ||
loadmill.run("./load-tests/simple.json") | ||
.then(id => console.log("Load test started: ", id)) | ||
.catch(err => console.error("Something bad: ", err)); | ||
``` | ||
@@ -64,8 +82,23 @@ ### Waiting for Tests | ||
```js | ||
/** | ||
* @returns {id: string, type: 'load' | 'test-suite', passed: boolean, url: string} | ||
*/ | ||
loadmill.run("./load-tests/long_test.json") | ||
.then(loadmill.wait) | ||
// -> {id: string, type: 'load', passed: boolean, url: string} | ||
.then(result => console.log(result)); | ||
// promise with async/await | ||
const loadTestId = await loadmill.run({ requests: [{ url: "www.myapp.com" }] }); | ||
const result = await loadmill.wait(loadTestId); | ||
``` | ||
### Promises vs Callbacks | ||
Every function that accepts a callback will return a promise instead if no callback is provided (and vice versa): | ||
```js | ||
loadmill.run("./load-tests/simple.json") | ||
.then(id => console.log("Load test started: ", id)) | ||
.catch(err => console.error("Something bad: ", err)); | ||
``` | ||
### Running multiple tests | ||
@@ -77,72 +110,13 @@ | ||
```js | ||
/** | ||
* @returns [{id: string, type: 'load', passed: boolean, url: string}] | ||
*/ | ||
loadmill.runFolder("/path/to/tests/folder") | ||
// -> [{id: string, type: 'load', passed: boolean, url: string}] | ||
.then(results => console.log(results)); | ||
``` | ||
### Functional Tests | ||
You may also use a test configuration to run a functional test (i.e. a single iteration of requests) - this is usually useful for testing your API for regressions after every new deployment. | ||
Functional tests are expected to be shorter and thus are awaited on by default: | ||
```js | ||
loadmill.runFunctional("./load-tests/api_test.json") | ||
// -> {id: string, type: 'functional', passed: boolean, url: string} | ||
.then(result => console.log(result)); | ||
``` | ||
If you wish to execute the tests from your local machine (rather than our SaaS infrastructure) you can use: | ||
```js | ||
loadmill.runFunctionalLocally("./load-tests/api_test.json") | ||
// -> {type: 'functional', passed: boolean} | ||
.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)); | ||
``` | ||
In case you wish to run several functional tests in a given folder you can use the `runFunctionalFolder` API. | ||
It will execute all the tests in the folder **synchronously** unless a test has failed. | ||
This API returns an array of the tests result: | ||
```js | ||
loadmill.runFunctionalFolder("/path/to/tests/folder") | ||
// -> [{id: string, type: 'functional', passed: boolean, url: string}] | ||
.then(result => console.log(result)); | ||
``` | ||
If you wish to execute all the tests in that folder from your local machine (rather than our SaaS infrastructure) you can use: | ||
```js | ||
loadmill.runFunctionalFolderLocally("/path/to/tests/folder") | ||
// -> [{type: 'functional', passed: boolean}] | ||
.then(result => console.log(result)); | ||
``` | ||
### Test Suites | ||
You may also launch an existing test suite by supplying the suite id - this is usually useful for testing your API for regressions after every new deployment. | ||
Test suites are launched and not awaiting the results. | ||
You can add an additional description at the end of the current suite's description by supplying the optional `additionalDescription` field. | ||
You can explicitly wait for a test to finish using the `wait` function: | ||
```js | ||
loadmill.runTestSuite({ | ||
id: "test-suite-uuid", | ||
additionalDescription: "description to add" | ||
}) | ||
// -> [{id: string}] | ||
.then(result => console.log(result)); | ||
``` | ||
### Parameters | ||
You will usually want some part of your test to be _dynamic_, e.g. the host name of the tested server. | ||
With Loadmill, this is made easy by using [parameters](https://docs.loadmill.com/test-scenarios/parameters). | ||
With Loadmill, this is made easy by using [parameters](https://docs.loadmill.com/api-testing/test-suite-editor/parameters). | ||
You may set/override parameter defaults for a test by passing a hash mapping parameter names to values: | ||
@@ -152,5 +126,2 @@ ```js | ||
loadmill.run("./load-tests/parametrized_test.json", {host: "test.myapp.com", port: 4443}, (err, id) => {/*...*/}); | ||
// You may also use predefined parameter values as well: | ||
loadmill.runFunctional("./load-tests/parametrized_test.json", {host: "test.${parentDomain}"}); | ||
``` | ||
@@ -162,22 +133,24 @@ | ||
``` | ||
loadmill <config-file-or-folder | test-suite-id> -t <token> [options] [parameter=value...] | ||
loadmill <load-config-file-or-folder | test-suite-id> -t <token> [options] [parameter=value...] | ||
``` | ||
### Functional Tests | ||
### Test suites | ||
The default is to run a functional test: | ||
You may launch a test suite by setting the `-s` or `--test-suite` option: | ||
``` | ||
loadmill test.json --token DW2rTlkNmE6A3ax5LVTSDxv2Jfw4virjQpmbOaLG | ||
loadmill test-suite-id --test-suite -t DW2rTlkNmE6A3ax5LVTSDxv2Jfw4virjQpmbOaLG | ||
``` | ||
Unless the `-q` option is set, the result JSON will be printed to the standard output. | ||
The test suite will be launched and its unique identifier will be printed to the standard output. You may alternatively | ||
set the `-w` or `--wait` option in order to wait for the test-suite to finish, in which case only the result JSON will be | ||
printed out at the end | ||
### Local Functional Tests | ||
You can add an additional description at the end of the current suite's description with the `--additional-description <description>` option. | ||
You can also run *functional* tests from your local machine using the `-c` or `--local` flag | ||
You can tell loadmill to run flows that are assigned to a specific label with the `--labels <labels>` option. Multiple labels can be provided by seperated them with "," (e.g. 'label1,label2'). | ||
``` | ||
loadmill local-test.json --local --token DW2rTlkNmE6A3ax5LVTSDxv2Jfw4virjQpmbOaLG | ||
loadmill <test-suite-id> --test-suite -t <token> --labels "label1,label2" | ||
``` | ||
Using the `-c` or `--local` option will override other options like `--load-test` etc... | ||
@@ -198,15 +171,2 @@ ### Load Tests | ||
### Test suites | ||
You may launch a test suite by setting the `-s` or `--test-suite` option: | ||
``` | ||
loadmill test-suite-id --test-suite -t DW2rTlkNmE6A3ax5LVTSDxv2Jfw4virjQpmbOaLG | ||
``` | ||
The test suite will be launched and its unique identifier will be printed to the standard output. You may alternatively | ||
set the `-w` or `--wait` option in order to wait for the load test to finish, in which case only the result JSON will be | ||
printed out at the end | ||
You can add an additional description at the end of the current suite's description by supplying the optional `--additional-description <description>` option. | ||
### Exit Status | ||
@@ -230,11 +190,10 @@ | ||
- `-t, --token <token>` Provide a Loadmill API Token. You must provide a token in order to run tests. | ||
- `-l, --load-test` Launch a load test. If not set, a functional test will run instead. | ||
- `-l, --load-test` Launch a load test. | ||
- `-s, --test-suite` Launch a test suite. If set then a test suite id must be provided instead of config file. | ||
- `--additional-description <description>` Add an aditional description at the end of the current suite's description - available only for test suites. | ||
- `-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. | ||
- `--additional-description <description>` Add an additional description at the end of the current suite's description - available only for test suites. | ||
- `--labels <labels>`, Run flows that are assigned to a specific label. Multiple labels can be provided by seperated them with "," (e.g. 'label1,label2'). | ||
- `-w, --wait` Wait for the test to finish. | ||
- `-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`). In case of an error will print the entire test's requests otherwise will print only the failed request. | ||
- `--colors` Print test results in color. | ||
- `-c, --local` Execute functional test synchronously on local machine. This flag overrides load-test and async options. | ||
- `--colors` Print test results in color. |
@@ -176,3 +176,3 @@ import './polyfills' | ||
let suiteId, additionalDescription; | ||
let suiteId, additionalDescription, labels; | ||
if (typeof suite === 'string') { // need to depricate the option of string in 2.x version | ||
@@ -183,2 +183,3 @@ suiteId = suite; | ||
additionalDescription = suite.additionalDescription; | ||
labels = suite.labels; | ||
} | ||
@@ -194,3 +195,3 @@ | ||
} = await superagent.post(`${testingServer}/api/test-suites/${suiteId}/run`) | ||
.send({ overrideParameters, additionalDescription }) | ||
.send({ overrideParameters, additionalDescription, labels }) | ||
.auth(token, ''); | ||
@@ -402,2 +403,3 @@ | ||
additionalDescription: string; | ||
labels?: string[] | null; | ||
} | ||
@@ -404,0 +406,0 @@ |
import * as Loadmill from './index'; | ||
import * as program from 'commander'; | ||
import { getJSONFilesInFolderRecursively, Logger, isUUID, getObjectAsString } from './utils'; | ||
import { getJSONFilesInFolderRecursively, Logger, isUUID, getObjectAsString, convertStrToArr } from './utils'; | ||
program | ||
.usage("<config-file-or-folder | testSuiteId> -t <token> [options] [parameter=value...]") | ||
.usage("<load-config-file-or-folder | testSuiteId> -t <token> [options] [parameter=value...]") | ||
.description( | ||
@@ -13,9 +13,7 @@ "Run a load test or a test suite on loadmill.com.\n " + | ||
.option("-t, --token <token>", "Loadmill API Token. You must provide a token in order to run tests.") | ||
.option("-l, --load-test", "Launch a load test. If not set, a functional test will run instead.") | ||
.option("-l, --load-test", "Launch a load test.") | ||
.option("-s, --test-suite", "Launch a test suite. If set then a test suite id must be provided instead of config file.") | ||
.option("--additional-description <description>", "Add an aditional description at the end of the current suite's description - available only for test suites.") | ||
.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("--additional-description <description>", "Add an additional description at the end of the current suite's description - available only for test suites.") | ||
.option("--labels <labels>", "Run flows that are assigned to a specific label. Multiple labels can be provided by seperated them with "," (e.g. 'label1,label2').") | ||
.option("-w, --wait", "Wait for the test to finish.") | ||
.option("-n, --no-bail", "Return exit code 0 even if test fails.") | ||
@@ -25,3 +23,2 @@ .option("-q, --quiet", "Do not print out anything (except errors).") | ||
.option("--colors", "Print test results in color") | ||
.option("-c, --local", "Execute functional test synchronously on local machine. This flag trumps load-test and async options") | ||
.parse(process.argv); | ||
@@ -36,3 +33,3 @@ | ||
async function start() { | ||
let { | ||
@@ -50,2 +47,3 @@ wait, | ||
additionalDescription, | ||
labels, | ||
args: [input, ...rawParams] | ||
@@ -77,2 +75,3 @@ } = program; | ||
additionalDescription, | ||
labels, | ||
parameters, | ||
@@ -89,3 +88,3 @@ }); | ||
let res; | ||
const suite: Loadmill.TestSuiteDef = { id: input, additionalDescription }; | ||
const suite: Loadmill.TestSuiteDef = { id: input, additionalDescription, labels: convertStrToArr(labels) }; | ||
try { | ||
@@ -92,0 +91,0 @@ let running = await loadmill.runTestSuite(suite, parameters); |
@@ -7,3 +7,3 @@ import * as fs from "fs"; | ||
import * as Loadmill from "./index"; | ||
import {resolveExpression} from 'loadmill-runner'; | ||
import { resolveExpression } from 'loadmill-runner'; | ||
import * as util from 'util'; | ||
@@ -47,2 +47,5 @@ | ||
export const convertStrToArr = (strWithCommas) => { | ||
return typeof strWithCommas !== "string" ? null : strWithCommas.split(","); | ||
} | ||
@@ -56,3 +59,3 @@ const printRequest = (trialRes, assertionErrorsPerRequest, testArgs, logger) => { | ||
for (let requestIndex in assertionErrorsPerRequest) { | ||
logger.log(getObjectAsString(trialRes.resolvedRequests[requestIndex], testArgs && testArgs.colors)) ; | ||
logger.log(getObjectAsString(trialRes.resolvedRequests[requestIndex], testArgs && testArgs.colors)); | ||
} | ||
@@ -66,3 +69,3 @@ } | ||
let assertionErrorsPerRequest = getAssertionErrors(trialRes); | ||
if (!isEmptyObj(assertionErrorsPerRequest)) { | ||
@@ -96,3 +99,3 @@ logger.error('Test failed - ' + description); | ||
const actualParameterValue = actualParameter ? actualParameter[parameterName] : undefined; | ||
// to do, eval the assertion expression to the actual string | ||
@@ -140,3 +143,3 @@ let assertionMismatch = "be not empty or true" | ||
/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(s); | ||
export class Logger { | ||
@@ -143,0 +146,0 @@ private readonly verb: boolean = false; |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 3 instances in 1 package
16
1521
87277
3
190
7