Socket
Socket
Sign inDemoInstall

serverless-test-plugin

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

serverless-test-plugin - npm Package Compare versions

Comparing version 0.1.5 to 0.2.0

.jshintrc

319

index.js

@@ -7,20 +7,16 @@ 'use strict';

module.exports = function(ServerlessPlugin, serverlessPath) { // Always pass in the ServerlessPlugin Class
module.exports = function(S) {
const path = require('path'),
fs = require('fs'),
BbPromise = require('bluebird'),
const BbPromise = require('bluebird'),
SCli = require(S.getServerlessPath('utils/cli')),
SError = require(S.getServerlessPath('Error')),
chalk = require('chalk'),
SError = require(path.join(serverlessPath, 'ServerlessError')),
SUtils = require(path.join(serverlessPath, 'utils')),
SCli = require( path.join( serverlessPath, 'utils', 'cli' ) ),
context = require( path.join( serverlessPath, 'utils', 'context' ) ),
JUnitWriter = require("junitwriter"),
intercept = require("intercept-stdout");
JUnitWriter = require('junitwriter'),
intercept = require('intercept-stdout');
/**
* ServerlessPluginBoierplate
* ServerlessTestPlugin
*/
class ServerlessTestPlugin extends ServerlessPlugin {
class ServerlessTestPlugin extends S.classes.Plugin {

@@ -32,4 +28,4 @@ /**

constructor(S) {
super(S);
constructor() {
super();
}

@@ -56,3 +52,3 @@

this.S.addAction(this._runFunctionTest.bind(this), {
S.addAction(this._runFunctionTest.bind(this), {
handler: 'runFunctionTest',

@@ -65,12 +61,20 @@ description: 'Run tests on a given function',

shortcut: 'a',
description: 'Test all functions'
description: 'Optional - Test all functions'
},{
option: 'out',
shortcut: 'o',
description: 'JUnit output file'
description: 'Optional - JUnit output file'
},{
option: 'stage',
shortcut: 's',
description: 'The stage used to populate your templates. Default: the first stage found in your project'
},{
option: 'region',
shortcut: 'r',
description: 'The region used to populate your templates. Default: the first region for the first stage found.'
}],
parameters: [{ // Use paths when you multiple values need to be input (like an array). Input looks like this: "serverless custom run module1/function1 module1/function2 module1/function3. Serverless will automatically turn this into an array and attach it to evt.options within your plugin
parameter: 'paths',
description: 'One or multiple paths to your function',
position: '0->' // Can be: 0, 0-2, 0-> This tells Serverless which params are which. 3-> Means that number and infinite values after it.
parameters: [{
parameter: 'names',
description: 'One or multiple function names',
position: '0->'
}]

@@ -102,165 +106,166 @@ });

let _this = this;
// Set an environment variable the invoked functions can check for
process.env.SERVERLESS_TEST = true;
return new BbPromise(function (resolve, reject) {
// Prepare result object
evt.data.result = { status: false };
// Set an environment variable the invoked functions can check for
process.env.SERVERLESS_TEST = true;
// Instantiate Classes
let functions;
if (evt.options.all) {
// Load all functions
functions = S.getProject().getAllFunctions();
}
else if (S.cli && evt.options.names && evt.options.names.length === 0) {
// no names or options so use cwd behavior
// will return all functions if none in cwd
functions = S.utils.getFunctionsByCwd(S.getProject().getAllFunctions());
}
else if (evt.options.names && evt.options.names.length > 0) {
// return by passed name(s)
functions = evt.options.names.map(name => {
const func = S.getProject().getFunction(name);
if (!func) {
throw new SError(`Function ${name} does not exist in your project`);
}
return func;
});
}
// Prepare result object
evt.data.result = { status: false };
if (!functions || functions.length === 0) {
throw new SError(`You need to specify either a function path or --all to test all functions`);
}
// Instantiate Classes
let functions;
if (evt.options.all) {
// Load all functions
functions = _this.S.state.getFunctions();
}
else if (evt.options.paths) {
// Load individual functions as specified in command line
functions = _this.S.state.getFunctions({ paths: evt.options.paths });
}
// Set stage and region
const stages = S.getProject().stages;
const stagesKeys = Object.keys(stages);
if (!stagesKeys.length) {
throw new SError(`We could not find a default stage for your project: it looks like your _meta folder is empty. If you cloned your project using git, try "sls project init" to recreate your _meta folder`);
}
if (!functions || functions.length === 0) {
return BbPromise.reject(new SError(
"You need to specify either a function path or --all to test all functions",
SError.errorCodes.INVALID_PROJECT_SERVERLESS
));
}
const stage = evt.options.stage || stagesKeys[0];
const stageVariables = stages[stage];
// Iterate all functions, execute their handler and
// write the results into a JUnit file...
let junitWriter = new JUnitWriter();
let count = 0, succeeded = 0, failed = 0;
BbPromise.each(functions, function(functionData) {
let functionTestSuite = junitWriter.addTestsuite(functionData._config.sPath);
count++;
const region = evt.options.region || Object.keys(stageVariables.regions)[0];
if (functionData.runtime === "nodejs") {
// Load function file & handler
let functionFile = functionData.handler.split('/').pop().split('.')[0];
let functionHandler = functionData.handler.split('/').pop().split('.')[1];
let functionPath = path.join(_this.S.config.projectPath, functionData._config.sPath);
functionFile = path.join(functionPath, (functionFile + '.js'));
// Iterate all functions, execute their handler and
// write the results into a JUnit file...
const junitWriter = new JUnitWriter();
let count = 0, succeeded = 0, failed = 0;
return BbPromise.each(functions, function(functionData) {
let functionTestSuite = junitWriter.addTestsuite(functionData.name);
count++;
// Fire function
let eventFile = (functionData.custom.test ?
functionData.custom.test.event : false) || "event.json";
let functionEvent = SUtils.readAndParseJsonSync(path.join(functionPath, eventFile));
if (functionData.runtime.substring(0, 6) === 'nodejs') {
// TODO Should we skip a function that's explicitly specified via command line option?
if (functionData.custom.test && functionData.custom.test.skip) {
SCli.log(`Skipping ${functionData._config.sPath}`);
functionTestSuite.addTestcase("skipped", functionData._config.sPath);
functionTestSuite.setSkipped(true);
return; // skip this function
}
// TODO Should we skip a function that's explicitly specified via command line option?
if (functionData.custom.test && functionData.custom.test.skip) {
SCli.log(`Skipping ${functionData.name}`);
functionTestSuite.addTestcase('skipped', functionData.name);
functionTestSuite.setSkipped(true);
return BbPromise.resolve(); // skip this function
}
return new BbPromise(function(resolve) {
try {
// Load the handler code
functionHandler = require(functionFile)[functionHandler];
if (!functionHandler) {
let msg = `Handler function ${functionData.handler} not found`;
SCli.log(chalk.bold(msg));
evt.data.result.status = 'error';
evt.data.result.response = msg;
return resolve();
}
// Load test event data
const eventFile = functionData.getRootPath((functionData.custom.test ?
functionData.custom.test.event : false) || 'event.json');
const eventData = S.utils.readFileSync(eventFile);
// Okay, let's go and execute the handler
// We intercept all stdout from the function and dump
// it into our test results instead.
SCli.log(`Testing ${functionData._config.sPath}...`);
let testCase = functionTestSuite.addTestcase("should succeed", functionData._config.sPath);
let capturedText = "";
let unhookIntercept = intercept(function(txt) {
capturedText += txt;
});
let startTime = Date.now();
functionHandler(functionEvent, context(functionData.name, function (err, result) {
try {
// We intercept all stdout from the function and dump
// it into our test results instead.
SCli.log(`Testing ${functionData.name}...`);
let testCase = functionTestSuite.addTestcase('should succeed', functionData.name);
let capturedText = '';
let unhookIntercept = intercept(function(txt) {
// Remove all ANSI color codes from output
const regex = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
capturedText += txt.replace(regex, '');
return ''; // don't print anything
});
let duration = (Date.now() - startTime) / 1000;
unhookIntercept(); // stop intercepting stdout
// Finally run the Lambda function...
let startTime = Date.now();
return functionData.run(stage, region, eventData)
.then(function() {
testCase.setSystemOut(capturedText);
testCase.setTime(duration);
let duration = (Date.now() - startTime) / 1000;
unhookIntercept(); // stop intercepting stdout
// Show error
if (err) {
testCase.addFailure(err.toString(), "Failed");
testCase.setSystemOut(capturedText);
testCase.setTime(duration);
// Done with errors.
SCli.log(chalk.bgRed.white(" ERROR ") + " " +
chalk.red(err.toString()));
failed++;
}
else if (duration > functionData.timeout) {
let msg = `Timeout of ${functionData.timeout} seconds exceeded`;
testCase.addFailure(msg, "Timeout");
if (duration > functionData.timeout) {
let msg = `Timeout of ${functionData.timeout} seconds exceeded`;
testCase.addFailure(msg, "Timeout");
SCli.log(chalk.bgMagenta.white(" TIMEOUT ") + " " +
chalk.magenta(msg));
failed++;
}
else {
// Done.
SCli.log(chalk.green("Success!"));
succeeded++;
}
return resolve();
}));
SCli.log(chalk.bgMagenta.white(" TIMEOUT ") + " " +
chalk.magenta(msg));
failed++;
}
catch (err) {
else {
// Done.
SCli.log(chalk.green("Success!"));
succeeded++;
}
})
.catch(function(err) {
unhookIntercept(); // stop intercepting stdout
testCase.addFailure(err.toString(), "Failed");
SCli.log("-----------------");
SCli.log(chalk.bold("Failed to Run Handler - This Error Was Thrown:"));
SCli.log(err);
evt.data.result.status = 'error';
evt.data.result.response = err.message;
return resolve();
}
// Done with errors.
SCli.log(chalk.bgRed.white(" ERROR ") + " " +
chalk.red(err.toString()));
failed++;
});
}
else {
SCli.log("Skipping " + functionData._config.sPath);
functionTestSuite.setSkipped(true);
catch (err) {
SCli.log("-----------------");
SCli.log(chalk.bold("Failed to Run Handler - This Error Was Thrown:"));
SCli.log(err);
evt.data.result.status = 'error';
evt.data.result.response = err.message;
}
}).then(function() {
}
else {
SCli.log("Skipping " + functionData.name);
functionTestSuite.setSkipped(true);
}
})
.then(function() {
SCli.log("-----------------");
SCli.log("-----------------");
// All done. Print a summary and write the test results
SCli.log("Tests completed: " +
chalk.green(String(succeeded) + " succeeded") + " / " +
chalk.red(String(failed) + " failed") + " / " +
chalk.white(String(count - succeeded - failed) + " skipped"));
// All done. Print a summary and write the test results
SCli.log("Tests completed: " +
chalk.green(String(succeeded) + " succeeded") + " / " +
chalk.red(String(failed) + " failed") + " / " +
chalk.white(String(count - succeeded - failed) + " skipped"));
if (evt.options.out) {
// Write test results to file
return new BbPromise(function(resolve) {
junitWriter.save(evt.options.out, function() {
SCli.log("Test results written to " + evt.options.out);
resolve();
});
if (evt.options.out) {
// Write test results to file
return new BbPromise(function(resolve) {
junitWriter.save(evt.options.out, function() {
SCli.log("Test results written to " + evt.options.out);
resolve();
});
}
}).then(function() {
resolve();
process.exit(); // FIXME force exit
}).catch(function(err) {
});
}
})
.then(function() {
process.exit(); // FIXME force exit
})
.catch(function(err) {
SCli.log("-----------------");
SCli.log("-----------------");
SCli.log(chalk.bold("Failed to Run Tests - This Error Was Thrown:"));
SCli.log(err);
evt.data.result.status = 'error';
evt.data.result.response = err.message;
return resolve();
}).finally(function() {
process.env.SERVERLESS_TEST = undefined;
});
SCli.log(chalk.bold("Failed to Run Tests - This Error Was Thrown:"));
SCli.log(err);
evt.data.result.status = 'error';
evt.data.result.response = err.message;
})
.finally(function() {
process.env.SERVERLESS_TEST = undefined;
});

@@ -267,0 +272,0 @@ }

{
"name": "serverless-test-plugin",
"version": "0.1.5",
"version": "0.2.0",
"engines": {

@@ -30,10 +30,9 @@ "node": ">=4.0"

"bin": {},
"devDependencies": {
},
"devDependencies": {},
"dependencies": {
"chalk": "^1.1.0",
"bluebird": "^3.0.6",
"junitwriter": "^0.3.1",
"intercept-stdout": "^0.1.2"
"bluebird": "^3.3.5",
"chalk": "^1.1.3",
"intercept-stdout": "^0.1.2",
"junitwriter": "^0.3.1"
}
}

@@ -21,3 +21,3 @@ #Serverless Test Plugin

**Note:** Serverless *v0.1.4* or higher is required.
**Note:** Serverless *v0.5.0* or higher is required.

@@ -49,10 +49,14 @@

Test an individual function:
```
serverless function test <function>
```
To test all functions in the current path, invoke the plugin without any function name:
```
serverless function test <component>/<module>/<function>
serverless function test
```
Test all functions in the project:
To test all functions in the project specify the `--all` parameter:
```

@@ -63,4 +67,11 @@ serverless function test --all

Test all functions and output results into a JUnit compatible XML:
You can also specify a stage and/or a region for your tests. If none is specified, the
first stage and region defined in your `_meta` folder will be used:
```
serverless function test <function> --stage dev --region us-east-1
```
To test all functions and output results into a JUnit compatible XML, specify the
`--out` parameter with a target file name:
```

@@ -71,8 +82,14 @@ serverless function test --all --out test_results/report.xml

To detect whether your code runs in a test environment or not, check for the `SERVERLESS_TEST` environment variable:
Sometimes it is desirable to mock certain behavior in your code depending on whether it is running in a
test automation script or on an actual server. For this reason the `serverless-test-plugin`
introduces a dedicated environment variable `SERVERLESS_TEST`:
```
if (process.env.SERVERLESS_TEST) {
console.log("This code runs as part of an intergration test.");
console.log("This code runs as part of an integration test.");
}
else {
console.log("This code does NOT run as part of an integration test..")
}
```

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc