Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Add npm test command during npm build #681

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from
30 changes: 30 additions & 0 deletions aws_lambda_builders/workflows/nodejs_npm/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""

import logging
import os
from typing import Optional

from aws_lambda_builders.actions import ActionFailedError, BaseAction, Purpose
Expand Down Expand Up @@ -321,3 +322,32 @@ def execute(self):

except OSError as ex:
raise ActionFailedError(str(ex))


class NodejsNpmTestAction(NodejsNpmInstallOrUpdateBaseAction):
"""
A Lambda Builder Action that runs tests in NPM project
"""

NAME = "NpmTest"
DESCRIPTION = "Running tests from NPM"

def execute(self):
"""
Runs the action if environment variable `SAM_NPM_RUN_TEST_WITH_BUILD` is `true`.

:raises lambda_builders.actions.ActionFailedError: when NPM execution fails
"""
try:
is_run_test_with_build = os.getenv("SAM_NPM_RUN_TEST_WITH_BUILD", "False")
if is_run_test_with_build == "true":
LOG.debug("NODEJS running tests in: %s", self.install_dir)

command = ["test", "--if-present"]
self.subprocess_npm.run(command, cwd=self.install_dir)
else:
LOG.debug("NODEJS skipping tests")
LOG.debug("Add env variable 'SAM_NPM_RUN_TEST_WITH_BUILD=true' to run tests with build")

except NpmExecutionError as ex:
raise ActionFailedError(str(ex))
8 changes: 8 additions & 0 deletions aws_lambda_builders/workflows/nodejs_npm/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
NodejsNpmPackAction,
NodejsNpmrcAndLockfileCopyAction,
NodejsNpmrcCleanUpAction,
NodejsNpmTestAction,
NodejsNpmUpdateAction,
)
from aws_lambda_builders.workflows.nodejs_npm.npm import SubprocessNpm
Expand Down Expand Up @@ -123,6 +124,13 @@ def __init__(self, source_dir, artifacts_dir, scratch_dir, manifest_path, runtim
)
)

self.actions.append(
NodejsNpmTestAction(
install_dir=self.manifest_dir if is_building_in_source and is_external_manifest else self.build_dir,
subprocess_npm=subprocess_npm,
)
)

if is_building_in_source and is_external_manifest:
# Since we run `npm install` in the manifest directory, so we need to link the node_modules directory in
# the source directory.
Expand Down
32 changes: 32 additions & 0 deletions tests/integration/workflows/nodejs_npm/test_nodejs_npm.py
Original file line number Diff line number Diff line change
Expand Up @@ -779,3 +779,35 @@ def test_builds_project_with_manifest_outside_root_and_local_dependencies_with_d
# expected dependencies in source directory
source_modules = set(os.listdir(os.path.join(source_dir, "node_modules")))
self.assertTrue(all(expected_module in source_modules for expected_module in expected_modules))

@parameterized.expand([("nodejs16.x",), ("nodejs18.x",), ("nodejs20.x",)])
def test_runs_test_script_if_specified(self, runtime):
source_dir = os.path.join(self.TEST_DATA_FOLDER, "test-script-to-create-file")

self.builder.build(
source_dir,
self.artifacts_dir,
self.scratch_dir,
os.path.join(source_dir, "package.json"),
runtime=runtime,
)

expected_files = {"package.json", "created.js"}
output_files = set(os.listdir(self.artifacts_dir))
self.assertEqual(expected_files, output_files)

@parameterized.expand([("nodejs16.x",), ("nodejs18.x",), ("nodejs20.x",)])
def test_does_not_raise_error_if_empty_test_script(self, runtime):
source_dir = os.path.join(self.TEST_DATA_FOLDER, "empty-test-script")

self.builder.build(
source_dir,
self.artifacts_dir,
self.scratch_dir,
os.path.join(source_dir, "package.json"),
runtime=runtime,
)

expected_files = {"package.json"}
output_files = set(os.listdir(self.artifacts_dir))
self.assertEqual(expected_files, output_files)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "testscript",
"version": "1.0.0",
"description": "",
"scripts": {
"test": ""
},
"keywords": [],
"author": "",
"license": "APACHE2.0"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "testscript",
"version": "1.0.0",
"description": "",
"scripts": {
"test": "touch created.js"
},
"keywords": [],
"author": "",
"license": "APACHE2.0"
}
41 changes: 41 additions & 0 deletions tests/unit/workflows/nodejs_npm/test_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
NodejsNpmrcCleanUpAction,
NodejsNpmLockFileCleanUpAction,
NodejsNpmCIAction,
NodejsNpmTestAction,
)
from aws_lambda_builders.workflows.nodejs_npm.npm import NpmExecutionError

Expand Down Expand Up @@ -219,3 +220,43 @@ def test_raises_action_failed_when_removing_fails(self, OSUtilMock):

with self.assertRaises(ActionFailedError):
action.execute()


class TestNodejsNpmTestAction(TestCase):
@patch("aws_lambda_builders.workflows.nodejs_npm.npm.SubprocessNpm")
@patch.dict("os.environ", {"SAM_NPM_RUN_TEST_WITH_BUILD": "true"}, clear=True)
def test_runs_npm_test_for_npm_project_if_env_var_true(self, SubprocessNpmMock):
subprocess_npm = SubprocessNpmMock.return_value

action = NodejsNpmTestAction(install_dir="tests", subprocess_npm=subprocess_npm)

action.execute()

expected_args = ["test", "--if-present"]

subprocess_npm.run.assert_called_with(expected_args, cwd="tests")

@patch("aws_lambda_builders.workflows.nodejs_npm.npm.SubprocessNpm")
def test_does_not_run_npm_test_for_npm_project_if_no_env_var(self, SubprocessNpmMock):
subprocess_npm = SubprocessNpmMock.return_value

action = NodejsNpmTestAction(install_dir="tests", subprocess_npm=subprocess_npm)

action.execute()

assert not subprocess_npm.run.called

@patch("aws_lambda_builders.workflows.nodejs_npm.npm.SubprocessNpm")
@patch.dict("os.environ", {"SAM_NPM_RUN_TEST_WITH_BUILD": "true"}, clear=True)
def test_raises_action_failed_when_npm_test_fails(self, SubprocessNpmMock):
subprocess_npm = SubprocessNpmMock.return_value

builder_instance = SubprocessNpmMock.return_value
builder_instance.run.side_effect = NpmExecutionError(message="boom!")

action = NodejsNpmTestAction("artifacts", subprocess_npm=subprocess_npm)

with self.assertRaises(ActionFailedError) as raised:
action.execute()

self.assertEqual(raised.exception.args[0], "NPM Failed: boom!")
Loading