Merge "Trigger tests for luci-test repo as part of CI" into main
diff --git a/recipes/README.recipes.md b/recipes/README.recipes.md
index 4351976..762c20f 100644
--- a/recipes/README.recipes.md
+++ b/recipes/README.recipes.md
@@ -16,15 +16,16 @@
 — **def [RunSteps](/recipes/recipes/hello_world.py#11)(api):**
 ### *recipes* / [luci-test](/recipes/recipes/luci-test.py)
 
-[DEPS](/recipes/recipes/luci-test.py#3): [depot\_tools/bot\_update][depot_tools/recipe_modules/bot_update], [depot\_tools/gclient][depot_tools/recipe_modules/gclient], [recipe\_engine/buildbucket][recipe_engine/recipe_modules/buildbucket], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/path][recipe_engine/recipe_modules/path]
+[DEPS](/recipes/recipes/luci-test.py#7): [depot\_tools/bot\_update][depot_tools/recipe_modules/bot_update], [depot\_tools/gclient][depot_tools/recipe_modules/gclient], [recipe\_engine/buildbucket][recipe_engine/recipe_modules/buildbucket], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/nodejs][recipe_engine/recipe_modules/nodejs], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/step][recipe_engine/recipe_modules/step]
 
 PYTHON_VERSION_COMPATIBILITY: PY3
 
-— **def [RunSteps](/recipes/recipes/luci-test.py#13)(api):**
+— **def [RunSteps](/recipes/recipes/luci-test.py#19)(api):**
 
 [depot_tools/recipe_modules/bot_update]: https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/e1c8efebe0a3cce42ca46d6057b6d4bd909ad203/recipes/README.recipes.md#recipe_modules-bot_update
 [depot_tools/recipe_modules/gclient]: https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/e1c8efebe0a3cce42ca46d6057b6d4bd909ad203/recipes/README.recipes.md#recipe_modules-gclient
 [recipe_engine/recipe_modules/buildbucket]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/a4e5d51c4351ab0674e264a8a360572286b04a6f/README.recipes.md#recipe_modules-buildbucket
 [recipe_engine/recipe_modules/context]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/a4e5d51c4351ab0674e264a8a360572286b04a6f/README.recipes.md#recipe_modules-context
+[recipe_engine/recipe_modules/nodejs]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/a4e5d51c4351ab0674e264a8a360572286b04a6f/README.recipes.md#recipe_modules-nodejs
 [recipe_engine/recipe_modules/path]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/a4e5d51c4351ab0674e264a8a360572286b04a6f/README.recipes.md#recipe_modules-path
 [recipe_engine/recipe_modules/step]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/a4e5d51c4351ab0674e264a8a360572286b04a6f/README.recipes.md#recipe_modules-step
diff --git a/recipes/recipes/luci-test.expected/basic.json b/recipes/recipes/luci-test.expected/basic.json
index 10ab1c7..2cdb098 100644
--- a/recipes/recipes/luci-test.expected/basic.json
+++ b/recipes/recipes/luci-test.expected/basic.json
@@ -17,7 +17,7 @@
       "-o",
       "DOWNLOAD_COMMANDS"
     ],
-    "cwd": "[CACHE]/builder",
+    "cwd": "[CACHE]/builder/src",
     "env": {
       "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
     },
@@ -78,7 +78,7 @@
       "--refs",
       "refs/heads/main"
     ],
-    "cwd": "[CACHE]/builder",
+    "cwd": "[CACHE]/builder/src",
     "env": {
       "DEPOT_TOOLS_REPORT_BUILD": "gerrit/try/builder/8945511751514863184",
       "GIT_HTTP_LOW_SPEED_LIMIT": "102400",
@@ -145,6 +145,108 @@
     ]
   },
   {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[CACHE]/nodejs",
+      "-ensure-file",
+      "infra/3pp/tools/nodejs/${platform} version:2@8.3.0",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[CACHE]/builder/src",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "gerrit:try"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "ensure_installed",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-version:2@8.3.0-\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/3pp/tools/nodejs/resolved-platform\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "npm",
+      "install"
+    ],
+    "cwd": "[CACHE]/builder/src",
+    "env": {
+      "npm_config_cache": "[CACHE]/npmcache/npm",
+      "npm_config_prefix": "[CACHE]/npmcache/pfx"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[CACHE]/npmcache/pfx/bin",
+        "[CACHE]/nodejs/bin"
+      ]
+    },
+    "luci_context": {
+      "realm": {
+        "name": "gerrit:try"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "install deps"
+  },
+  {
+    "cmd": [
+      "npm",
+      "run test"
+    ],
+    "cwd": "[CACHE]/builder/src",
+    "env": {
+      "npm_config_cache": "[CACHE]/npmcache/npm",
+      "npm_config_prefix": "[CACHE]/npmcache/pfx"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[CACHE]/npmcache/pfx/bin",
+        "[CACHE]/nodejs/bin"
+      ]
+    },
+    "luci_context": {
+      "realm": {
+        "name": "gerrit:try"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "run tests"
+  },
+  {
     "name": "$result"
   }
 ]
\ No newline at end of file
diff --git a/recipes/recipes/luci-test.py b/recipes/recipes/luci-test.py
index 73a6c09..9aefb65 100644
--- a/recipes/recipes/luci-test.py
+++ b/recipes/recipes/luci-test.py
@@ -1,11 +1,17 @@
 PYTHON_VERSION_COMPATIBILITY = "PY3"
 
+# Reference: https://chromium.googlesource.com/infra/luci/recipes-py/+/HEAD/doc/
+
+# All the deps get combined into an `api` variable passed in to RunSteps and 
+# GenTests. Versions and urls are specified in `recipes.cfg`.
 DEPS = [
   'depot_tools/bot_update',
   'depot_tools/gclient',
   'recipe_engine/buildbucket',
   'recipe_engine/context',
   'recipe_engine/path',
+  'recipe_engine/step',
+  'recipe_engine/nodejs',
 ]
 
 # Check out the change and run the tests to verify the change as part of Change
@@ -13,43 +19,36 @@
 def RunSteps(api):
   cl = api.buildbucket.build.input.gerrit_changes[0]
 
-  gs_suffix = '-review.googlesource.com'
+  # Remove "-review" part of host URL
   host = cl.host
+  gs_suffix = '-review.googlesource.com'
   if host.endswith(gs_suffix):
     host = '%s.googlesource.com' % host[:-len(gs_suffix)]
 
   gclient_config = api.gclient.make_config()
   s = gclient_config.solutions.add()
   s.url = 'https://%s/%s' % (host, cl.project)
-  # name is the subfolder under api.path['cache'].join('builder') the
-  # repo will be checked out at. For simplicity, I've picked `src`.
-  #
-  # Note that gclient (and by extension, bot_update), allow for complicated
-  # multi-repo layouts and dependency inclusion (via "DEPS" files). There are
-  # other repo management tools and techniques available, but this one is what
-  # chrome currently uses and is tuned to have reasonable performance by default
-  # via caches.
+  # This is the name of the subfolder under api.path['cache'].join('builder') 
+  # where the repo will be checked out at.
   s.name = 'src'
   gclient_config.got_revision_mapping[s.name] = 'got_revision'
 
-  with api.context(cwd=api.path['cache'].join('builder')):
+  with api.context(cwd=api.path['cache'].join('builder').join(s.name)):
+    # Check out the code for the change, this is by default cached between
+    # builds for the same builder.
     update_result = api.bot_update.ensure_checkout(
         gclient_config=gclient_config)
+    with api.nodejs(version='8.3.0'):
+      # Named steps to test the change
+      api.step('install deps', ['npm', 'install'])
+      api.step('run tests', ['npm', 'run test'])
 
-  # At this point the code for the Gerrit CL is checked out at
-  # `api.path['cache'].join('builder')`, which by default is preserved locally
-  # on the bot machine and re-used between different builds for the same
-  # builder.
-
-  # TODO(frankborden): perform $ npm run test
 
 # Test the recipe and generate the expect json file.
 def GenTests(api):
   yield api.test(
     'basic',
-    # These are just to make the JSON expectation file data look closer to
-    # reality. Project and git_repo will be filled in "for real" by the LUCI
-    # Change Verifier service when it creates your build.
+    # Execute RunSteps with test data
     api.buildbucket.try_build(
       project="gerrit",
       git_repo="https://gerrit.googlesource.com/luci-test",