Skip to content

Commit

Permalink
test: add integration test for http server
Browse files Browse the repository at this point in the history
To keep our repository well-tested, this commit introduces
an e2e test for the http server rule. This is usually not done
for http servers/rules within Bazel, but it feels valuable and
also serves as a great example of how Chromium tests could work
within Bazel, with RBE etc.
  • Loading branch information
devversion committed Jan 25, 2022
1 parent b066f7b commit 3548719
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 9 deletions.
5 changes: 5 additions & 0 deletions .bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ build --incompatible_strict_action_env
run --incompatible_strict_action_env
test --incompatible_strict_action_env

# Do not build runfile forests by default. If an execution strategy relies on runfile
# forests, the forest is created on-demand. See: https://github.com/bazelbuild/bazel/issues/6627
# and https://github.com/bazelbuild/bazel/commit/03246077f948f2790a83520e7dccc2625650e6df. This
# also helps with: https://github.com/bazelbuild/bazel/issues/4327#issuecomment-922106293.
build --nobuild_runfile_links

################################
# Remote Execution Setup #
Expand Down
2 changes: 1 addition & 1 deletion bazel/http-server/index.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ http_server_rule = rule(
)

def http_server(name, testonly = False, port = 4200, tags = [], **kwargs):
"""Creates a HTTP server that can depend on individual bazel targets. The server uses
"""Creates an HTTP server that can depend on individual bazel targets. The server uses
bazel runfile resolution in order to work with Bazel package paths. e.g. developers can
request files through their manifest path: "my_workspace/src/dev-app/my-genfile"."""

Expand Down
2 changes: 1 addition & 1 deletion bazel/http-server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export class HttpServer {

if (resolvedPath === null) {
res.statusCode = 404;
res.end('Page not found');
res.end('Not found - Error 404');
return;
}

Expand Down
35 changes: 33 additions & 2 deletions bazel/http-server/test/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_test")
load("//bazel/http-server:index.bzl", "http_server")
load("//tools:defaults.bzl", "ts_library")

ts_library(
name = "test_lib",
name = "app_lib",
testonly = True,
srcs = ["main.ts"],
)
Expand All @@ -12,5 +13,35 @@ http_server(
testonly = True,
srcs = ["index.html"],
environment_variables = ["GOOGLE_MAPS_API_KEY"],
deps = [":test_lib"],
deps = [":app_lib"],
)

ts_library(
name = "test_lib",
testonly = True,
srcs = ["server-test.ts"],
deps = [
"@npm//@bazel/runfiles",
"@npm//@types/selenium-webdriver",
"@npm//@types/wait-on",
"@npm//selenium-webdriver",
"@npm//wait-on",
],
)

nodejs_test(
name = "test",
# Pass the chromium and chromedriver binaries as arguments to the test.
# These variables are made available by the toolchain alias.
args = [
"$(CHROMIUM)",
"$(CHROMEDRIVER)",
],
data = [
":server",
":test_lib",
"//bazel/browsers/chromium",
],
entry_point = ":server-test.ts",
toolchains = ["//bazel/browsers/chromium:toolchain_alias"],
)
8 changes: 5 additions & 3 deletions bazel/http-server/test/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@

/// <reference lib="dom" />

const span = document.createElement('span');
span.innerHTML = 'Hello!';
const spanEl = document.createElement('span');
const secretValue = (window as any)['GOOGLE_MAPS_API_KEY']!;

document.body.appendChild(span);
spanEl.innerHTML = `My key: ${secretValue}`;

document.body.appendChild(spanEl);
98 changes: 98 additions & 0 deletions bazel/http-server/test/server-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

import {runfiles} from '@bazel/runfiles';
import {Builder, By, WebDriver} from 'selenium-webdriver';
import {Options as ChromeOptions, ServiceBuilder} from 'selenium-webdriver/chrome';

import * as waitOn from 'wait-on';
import * as childProcess from 'child_process';

/**
* Test script that will start the test http server binary in a background process.
* Once the server is available and listening, Chromium from `bazel/browsers` is
* launched through Selenium to ensure that the environment variable inlining,
* actual resolution of JavaScript resources and `index.html` works as expected.
*/
async function runTest() {
const [chromiumRootpath, chromedriverRootpath] = process.argv.slice(2);

// Resolve chromium, chromedriver and the server binary to disk paths.
const chromiumPath = runfiles.resolveWorkspaceRelative(chromiumRootpath);
const chromedriverPath = runfiles.resolveWorkspaceRelative(chromedriverRootpath);
const serverBinPath = runfiles.resolveWorkspaceRelative('bazel/http-server/test/server');

const serverPort = 1234;
const serverHost = `127.0.0.1:${serverPort}`;

// Start test http server in background
const serverProcess = childProcess.spawn(serverBinPath, ['--port', `${serverPort}`], {
env: {...process.env, GOOGLE_MAPS_API_KEY: 'myPersonalSecret'},
stdio: 'inherit',
});

// Ensure the process gets killed, if the test terminates early.
process.on('exit', () => serverProcess.kill());

// Keep track of potentially launched webdriver instance, so that
// we can kill it when the test code errors unexpectedly.
let driver: WebDriver | null = null;

try {
// Wait for server to be ready, regardless of status code (404/200 or else)
await waitOn({
resources: [`http-get://${serverHost}`],
headers: {
'accept': 'text/html',
},
});

const service = new ServiceBuilder(chromedriverPath);
const options = new ChromeOptions()
.setChromeBinaryPath(chromiumPath)
.headless()
.addArguments('--no-sandbox');

driver = await new Builder()
.forBrowser('chrome')
.setChromeOptions(options)
.setChromeService(service)
.build();

await driver.get(`http://${serverHost}`);

let bodyText = await driver.findElement(By.css('body')).getText();

// Assert that the variable is inlined, and that the `index.html` renders.
if (bodyText !== 'Works My key: myPersonalSecret') {
throw Error(`Unexpected body: ${bodyText}`);
}

console.log('Valid text for index file: ', bodyText);

await driver.get(`http://${serverHost}/not-found.txt`);

bodyText = await driver.findElement(By.css('body')).getText();

if (bodyText !== 'Not found - Error 404') {
throw Error(`Unexpected text when requesting unknown resource: ${bodyText}`);
}

console.log('Valid text for unknown resource:', bodyText);
} finally {
await driver?.quit();

// Kill server process if we exit normally (no script termination).
serverProcess.kill();
}
}

runTest().catch((e) => {
console.error(e);
process.exit(1);
});
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
"@types/semver": "^7.3.6",
"@types/shelljs": "^0.8.8",
"@types/uuid": "^8.3.1",
"@types/wait-on": "^5.3.1",
"@types/which": "^2.0.1",
"@types/yargs": "^17.0.0",
"@types/yarnpkg__lockfile": "^1.1.5",
Expand All @@ -102,6 +103,7 @@
"protobufjs": "^6.11.2",
"rxjs": "^7.4.0",
"uglify-js": "^3.14.2",
"wait-on": "^6.0.0",
"zone.js": "^0.11.4"
}
}
62 changes: 60 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1206,6 +1206,18 @@
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.2.tgz#30aa825f11d438671d585bd44e7fd564535fc210"
integrity sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw==

"@hapi/hoek@^9.0.0":
version "9.2.1"
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.1.tgz#9551142a1980503752536b5050fd99f4a7f13b17"
integrity sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw==

"@hapi/topo@^5.0.0":
version "5.1.0"
resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012"
integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==
dependencies:
"@hapi/hoek" "^9.0.0"

"@istanbuljs/load-nyc-config@^1.0.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
Expand Down Expand Up @@ -1573,6 +1585,23 @@
colors "~1.2.1"
string-argv "~0.3.1"

"@sideway/address@^4.1.3":
version "4.1.3"
resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.3.tgz#d93cce5d45c5daec92ad76db492cc2ee3c64ab27"
integrity sha512-8ncEUtmnTsMmL7z1YPB47kPUq7LpKWJNFPsRzHiIajGC5uXlWGn+AmkYPcHNl8S4tcEGx+cnORnNYaw2wvL+LQ==
dependencies:
"@hapi/hoek" "^9.0.0"

"@sideway/formula@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c"
integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==

"@sideway/pinpoint@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df"
integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==

"@tootallnate/once@2":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf"
Expand Down Expand Up @@ -1966,6 +1995,13 @@
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc"
integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==

"@types/wait-on@^5.3.1":
version "5.3.1"
resolved "https://registry.yarnpkg.com/@types/wait-on/-/wait-on-5.3.1.tgz#bc5520d1d8b90b9caab1bef23315685ded73320d"
integrity sha512-2FFOKCF/YydrMUaqg+fkk49qf0e5rDgwt6aQsMzFQzbS419h2gNOXyiwp/o2yYy27bi/C1z+HgfncryjGzlvgQ==
dependencies:
"@types/node" "*"

"@types/which@^2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@types/which/-/which-2.0.1.tgz#27ecd67f915b7c3d6ba552135bb1eecd66e63501"
Expand Down Expand Up @@ -2463,7 +2499,7 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==

[email protected]:
[email protected], axios@^0.21.1:
version "0.21.4"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575"
integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==
Expand Down Expand Up @@ -5057,6 +5093,17 @@ jju@~1.4.0:
resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a"
integrity sha1-o6vicYryQaKykE+EpiWXDzia4yo=

joi@^17.4.0:
version "17.5.0"
resolved "https://registry.yarnpkg.com/joi/-/joi-17.5.0.tgz#7e66d0004b5045d971cf416a55fb61d33ac6e011"
integrity sha512-R7hR50COp7StzLnDi4ywOXHrBrgNXuUUfJWIR5lPY5Bm/pOD3jZaTwpluUXVLRWcoWZxkrHBBJ5hLxgnlehbdw==
dependencies:
"@hapi/hoek" "^9.0.0"
"@hapi/topo" "^5.0.0"
"@sideway/address" "^4.1.3"
"@sideway/formula" "^3.0.0"
"@sideway/pinpoint" "^2.0.0"

js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
Expand Down Expand Up @@ -6975,7 +7022,7 @@ rxjs@^5.5.6:
dependencies:
symbol-observable "1.0.1"

rxjs@^7.2.0, rxjs@^7.4.0:
rxjs@^7.1.0, rxjs@^7.2.0, rxjs@^7.4.0:
version "7.5.2"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.2.tgz#11e4a3a1dfad85dbf7fb6e33cbba17668497490b"
integrity sha512-PwDt186XaL3QN5qXj/H9DGyHhP3/RYYgZZwqBv9Tv8rsAaiwFH1IsJJlcgD37J7UW5a6O67qX0KWKS3/pu0m4w==
Expand Down Expand Up @@ -8093,6 +8140,17 @@ w3c-xmlserializer@^3.0.0:
dependencies:
xml-name-validator "^4.0.0"

wait-on@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-6.0.0.tgz#7e9bf8e3d7fe2daecbb7a570ac8ca41e9311c7e7"
integrity sha512-tnUJr9p5r+bEYXPUdRseolmz5XqJTTj98JgOsfBn7Oz2dxfE2g3zw1jE+Mo8lopM3j3et/Mq1yW7kKX6qw7RVw==
dependencies:
axios "^0.21.1"
joi "^17.4.0"
lodash "^4.17.21"
minimist "^1.2.5"
rxjs "^7.1.0"

watchpack@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.3.1.tgz#4200d9447b401156eeca7767ee610f8809bc9d25"
Expand Down

0 comments on commit 3548719

Please sign in to comment.