Skip to content
This repository has been archived by the owner on Sep 16, 2021. It is now read-only.

ts_web_test rule #72

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ filegroup(
srcs = glob([
"node_modules/**/*.js",
"node_modules/**/*.d.ts",
"node_modules/**/*.json"
"node_modules/**/*.json",
"node_modules/karma/bin/karma",
]),
visibility = ["//visibility:public"],
)
2 changes: 2 additions & 0 deletions defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ load("//internal:build_defs.bzl", _ts_library = "ts_library")
load("//internal:ts_config.bzl", _ts_config = "ts_config")
load("//internal/devserver:ts_devserver.bzl", _ts_devserver = "ts_devserver_macro")
load("//internal:ts_repositories.bzl", _ts_repositories = "ts_repositories")
load("//internal:ts_web_test.bzl", _ts_web_test = "ts_web_test_macro")

ts_library = _ts_library
ts_config = _ts_config
ts_repositories = _ts_repositories
ts_devserver = _ts_devserver
ts_web_test = _ts_web_test
20 changes: 20 additions & 0 deletions examples/testing/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
load("@build_bazel_rules_typescript//:defs.bzl", "ts_library", "ts_web_test")

ts_library(
name = "lib",
srcs = ["decrement.ts"],
)

ts_library(
name = "tests",
srcs = ["decrement.spec.ts"],
deps = [":lib"],
testonly = 1,
)

ts_web_test(
name = "testing",
deps = [
":tests",
],
)
7 changes: 7 additions & 0 deletions examples/testing/decrement.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {decrement} from './decrement';

describe('decrementing', () => {
it('should do that', () => {
expect(decrement(1)).toBe(0);
});
});
3 changes: 3 additions & 0 deletions examples/testing/decrement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function decrement(n: number) {
return n - 1;
}
20 changes: 20 additions & 0 deletions internal/karma/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package(default_visibility=["//visibility:public"])

exports_files(["test-main.js", "karma.conf.js"])

load("@build_bazel_rules_typescript//:defs.bzl", "ts_library")

ts_library(
name = "karma_concat_js",
srcs = glob(["*.ts"]),
module_name = "karma-concat-js",
)

load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary")

nodejs_binary(
name = "karma_bin",
entry_point = "karma/bin/karma",
data = [":karma_concat_js"],
node_modules = "@build_bazel_rules_typescript_karma_deps//:node_modules",
)
74 changes: 74 additions & 0 deletions internal/karma/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Concat all JS files before serving.
*/
import * as crypto from 'crypto';
import * as fs from 'fs';
import * as path from 'path';
import * as process from 'process';
import * as tmp from 'tmp';
///<reference types="lib.dom"/>

/**
* Return SHA1 of data buffer.
*/
function sha1(data) {
const hash = crypto.createHash('sha1');
hash.update(data);
return hash.digest('hex');
}

/**
* Entry-point for the Karma plugin.
*/
function initConcatJs(logger, emitter, basePath) {
const log = logger.create('framework.concat_js');

// Create a tmp file for the concat bundle that is automatically cleaned up on
// exit.
const tmpFile = tmp.fileSync({keep: false});

emitter.on('file_list_modified', files => {
const bundleFile = {
path: '/concatjs_bundle.js',
contentPath: tmpFile.name,
isUrl: false,
content: ''
} as any;
const included = [];

files.included.forEach(file => {
if (path.extname(file.originalPath) !== '.js') {
// Preserve all non-JS that were there in the included list.
included.push(file);
} else {
const relativePath = path.relative(basePath, file.originalPath);

// Remove 'use strict'.
let content = file.content.replace(/('use strict'|"use strict");?/,
'');
content = JSON.stringify(
content + '\n//# sourceURL=http://concatjs/base/' +
relativePath + '\n');
content = `//${relativePath}\neval(${content});\n`;
bundleFile.content += content;
}
});

bundleFile.sha = sha1(new Buffer(bundleFile.content));
bundleFile.mtime = new Date();
included.unshift(bundleFile);

files.included = included;
files.served.push(bundleFile);

log.debug('Writing concatjs bundle to tmp file %s',
bundleFile.contentPath);
fs.writeFileSync(bundleFile.contentPath, bundleFile.content);
});
}

(initConcatJs as any).$inject = ['logger', 'emitter', 'config.basePath'];

module.exports = {
'framework:concat_js': ['factory', initConcatJs]
};
56 changes: 56 additions & 0 deletions internal/karma/karma.conf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Karma configuration
// GENERATED BY Bazel
module.exports = function(config) {
if (process.env['IBAZEL_NOTIFY_CHANGES'] === 'y') {
// Tell karma to only listen for ibazel messages on stdin rather than watch all the input files
// This is from fork alexeagle/karma in the ibazel branch:
// https://github.com/alexeagle/karma/blob/576d262af50b10e63485b86aee99c5358958c4dd/lib/server.js#L172
config.set({watchMode: 'ibazel'});
}

config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: 'TMPL_runfiles_path',
files: [
'build_bazel_rules_typescript/internal/karma/test-main.js',
TMPL_files
],
plugins: ['karma-*', 'karma-concat-js'],
frameworks: ['jasmine', 'concat_js', 'requirejs'],

// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress'],


// web server port
port: 9876,


// enable / disable colors in the output (reporters and logs)
colors: true,


// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,


// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,

// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['Chrome'],

// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
// note: run_karma.sh may override this as a command-line option.
singleRun: false,

// Concurrency level
// how many browser should be started simultaneous
concurrency: Infinity
})
}
13 changes: 13 additions & 0 deletions internal/karma/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"description": "runtime dependences for ts_web_test rules",
"devDependencies": {
"@types/tmp": "0.0.33",
"jasmine-core": "2.8.0",
"karma": "alexeagle/karma#fa1a84ac881485b5657cb669e9b4e5da77b79f0a",
"karma-chrome-launcher": "2.2.0",
"karma-jasmine": "1.1.1",
"karma-requirejs": "1.1.0",
"requirejs": "2.3.5",
"tmp": "0.0.33"
}
}
24 changes: 24 additions & 0 deletions internal/karma/test-main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
var allTestFiles = []
var TEST_REGEXP = /(spec|test)\.js$/i

// Get a list of all the test files to include
Object.keys(window.__karma__.files).forEach(function (file) {
if (TEST_REGEXP.test(file)) {
// Normalize paths to RequireJS module names.
// If you require sub-dependencies of test files to be loaded as-is (requiring file extension)
// then do not normalize the paths
var normalizedTestModule = file.replace(/^\/base\/|\.js$/g, '');
allTestFiles.push(normalizedTestModule);
}
})

require.config({
// Karma serves files under /base, which is the basePath from your config file
baseUrl: '/base',

// load all test files
deps: allTestFiles,

// we have to kickoff jasmine, as it is asynchronous
callback: window.__karma__.start
})
4 changes: 4 additions & 0 deletions internal/ts_repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,7 @@ def ts_repositories():
name = "build_bazel_rules_typescript_devserver_deps",
package_json = "@build_bazel_rules_typescript//internal/devserver:package.json",
)
npm_install(
name = "build_bazel_rules_typescript_karma_deps",
package_json = "@build_bazel_rules_typescript//internal/karma:package.json",
)
102 changes: 102 additions & 0 deletions internal/ts_web_test.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
load("@build_bazel_rules_nodejs//internal:node.bzl",
"sources_aspect",
"expand_path_into_runfiles",
)

_CONF_TMPL = "//internal/karma:karma.conf.js"
_LOADER = "//internal/karma:test-main.js"

def _ts_web_test_impl(ctx):
conf = ctx.actions.declare_file(
"%s.conf.js" % ctx.label.name,
sibling=ctx.outputs.executable)

files = depset(ctx.files.srcs)
for d in ctx.attr.deps:
if hasattr(d, "node_sources"):
files += d.node_sources
elif hasattr(d, "files"):
files += d.files

TMPL_files = "\n".join([
" '%s'," % expand_path_into_runfiles(ctx, f.short_path)
for f in files
])

# root-relative (runfiles) path to the directory containing karma.conf
config_segments = len(conf.short_path.split("/"))

ctx.actions.expand_template(
output = conf,
template = ctx.file._conf_tmpl,
substitutions = {
"TMPL_runfiles_path": "/".join([".."] * config_segments),
"TMPL_files": TMPL_files,
"TMPL_workspace_name": ctx.workspace_name,
})

ctx.actions.write(
output = ctx.outputs.executable,
is_executable = True,
content = """#!/usr/bin/env bash
readonly KARMA={TMPL_karma}
readonly CONF={TMPL_conf}
export HOME=$(mktemp -d)
ARGV=( "start" $CONF )

# Detect that we are running as a test, by using a well-known environment
# variable. See go/test-encyclopedia
if [ ! -z "$TEST_TMPDIR" ]; then
ARGV+=( "--single-run" )
fi

$KARMA ${{ARGV[@]}}
""".format(TMPL_karma = ctx.executable._karma.short_path,
TMPL_conf = conf.short_path))
return [DefaultInfo(
runfiles = ctx.runfiles(
files = ctx.files.srcs + ctx.files.deps + [
conf,
ctx.file._loader,
],
transitive_files = files,
# Propagate karma_bin and its runfiles
collect_data = True,
),
)]

ts_web_test = rule(
implementation = _ts_web_test_impl,
test = True,
attrs = {
"srcs": attr.label_list(allow_files = ["js"]),
"deps": attr.label_list(
allow_files = True,
aspects = [sources_aspect],
),
"data": attr.label_list(cfg = "data"),
"_karma": attr.label(
default = Label("//internal/karma:karma_bin"),
executable = True,
cfg = "data",
single_file = False,
allow_files = True),
"_conf_tmpl": attr.label(
default = Label(_CONF_TMPL),
allow_files = True, single_file = True),
"_loader": attr.label(
default = Label(_LOADER),
allow_files = True, single_file = True),
},
)

# This macro exists only to modify the users rule definition a bit.
# DO NOT add composition of additional rules here.
def ts_web_test_macro(tags = [], data = [], **kwargs):
ts_web_test(
# Users don't need to know that this tag is required to run under ibazel
tags = tags + ["ibazel_notify_changes"],
# Our binary dependency must be in data[] for collect_data to pick it up
# FIXME: maybe we can just ask the attr._karma for its runfiles attr
data = data + ["@build_bazel_rules_typescript//internal/karma:karma_bin"],
**kwargs)
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"typescript": ">=2.4.2"
},
"devDependencies": {
"@bazel/ibazel": "^0.2.0",
"@types/jasmine": "^2.8.2",
"@types/node": "7.0.18",
"@types/source-map": "^0.5.1",
Expand Down
4 changes: 4 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
# yarn lockfile v1


"@bazel/ibazel@^0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@bazel/ibazel/-/ibazel-0.2.0.tgz#c119aef4344a789cef5e792caaee52264123e71c"

"@types/jasmine@^2.8.2":
version "2.8.2"
resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.8.2.tgz#6ae4d8740c0da5d5a627df725b4eed71b8e36668"
Expand Down