Re-merge branch 'implement/plugin-helpers-package' with history

This commit is contained in:
spalger 2018-02-08 14:37:52 -07:00
commit 78844d6aaf
46 changed files with 3789 additions and 3 deletions

View file

@ -27,7 +27,7 @@ module.exports = {
// files not transpiled by babel can't using things like object-spread
{
files: ['.eslintrc.js'],
files: ['.eslintrc.js', 'packages/kbn-plugin-helpers/**/*.js'],
rules: {
'prefer-object-spread/prefer-object-spread': 'off',
},

View file

@ -13,7 +13,7 @@ To get started copy and paste this example to `test/functional/config.js`:
["source","js"]
-----------
import { resolve } from 'path';
import { resolveKibanaPath } from '@elastic/plugin-helpers';
import { resolveKibanaPath } from '@kbn/plugin-helpers';
import { MyServiceProvider } from './services/my_service';
import { MyAppPageProvider } from './services/my_app_page;

View file

@ -0,0 +1,64 @@
# kibana-plugin-helpers
[![Apache License](https://img.shields.io/badge/license-apache_2.0-a9215a.svg)](https://raw.githubusercontent.com/elastic/kibana-plugin-helpers/master/LICENSE)
[![CircleCI](https://img.shields.io/circleci/project/github/elastic/kibana-plugin-helpers.svg)](https://circleci.com/gh/elastic/kibana-plugin-helpers/tree/master)
Just some helpers for kibana plugin devs.
This simple CLI has several tasks that plugin devs can run from to easily debug, test, or package kibana plugins.
```sh
$ plugin-helpers help
Usage: plugin-helpers [options] [command]
Commands:
start Start kibana and have it include this plugin
build [options] [files...] Build a distributable archive
test Run the server and browser tests
test:browser [options] Run the browser tests in a real web browser
test:server [files...] Run the server tests using mocha
Options:
-h, --help output usage information
-V, --version output the version number
```
## Versions
Plugin Helpers | Kibana
-------------- | ------
9.x | 7.0+ (`kibanaRoot` setting removed from `plugin-helpers`)
8.x | 7.0+
7.x | 4.6.x to 6.x (node 6+ only)
6.x | 4.6.x to 6.x
5.x | 4.x
## Configuration
`plugin-helpers` accepts a number of settings, which can be specified at runtime, or included in a `.kibana-plugin-helpers.json` file if you'd like to bundle those settings with your project.
It will also observe a `.kibana-plugin-helpers.dev.json`, much like Kibana does, which we encourage you to add to your `.gitignore` file and use for local settings that you don't intend to share. These "dev" settings will override any settings in the normal json config.
All configuration setting listed below can simply can be included in the json config files. If you intend to inline the command, you will need to convert the setting to snake case (ie. `skipArchive` becomes `--skip-archive`).
## Global settings
### Settings for `start`
Setting | Description
------- | -----------
`includePlugins` | Intended to be used in a config file, an array of additional plugin paths to include, absolute or relative to the plugin root
`*` | Any options/flags included will be passed unmodified to the Kibana binary
### Settings for `build`
Setting | Description
------- | -----------
`skipArchive` | Don't create the zip file, leave the build path alone
`buildDestination` | Target path for the build output, absolute or relative to the plugin root
`buildVersion` | Version for the build output
`kibanaVersion` | Kibana version for the build output (added to package.json)

View file

@ -0,0 +1,9 @@
#!/usr/bin/env node
const nodeMajorVersion = parseFloat(process.version.replace(/^v(\d+)\..+/, '$1'));
if (nodeMajorVersion < 6) {
console.error('FATAL: kibana-plugin-helpers requires node 6+');
process.exit(1);
}
require('../cli');

View file

@ -0,0 +1,87 @@
const program = require('commander');
const pkg = require('./package.json');
const run = require('./lib/run');
const docs = require('./lib/docs');
const enableCollectingUnknownOptions = require('./lib/enable_collecting_unknown_options');
function taskRunner(fn) {
return function actionWrapper() {
const args = [].slice.apply(arguments);
const command = args.pop();
fn.apply(null, [command].concat(args));
};
}
program
.version(pkg.version);
enableCollectingUnknownOptions(
program
.command('start')
.description('Start kibana and have it include this plugin')
.on('--help', docs('start'))
.action(taskRunner(function (command) {
run('start', {
flags: command.unknownOptions
});
}))
);
program
.command('build [files...]')
.description('Build a distributable archive')
.on('--help', docs('build'))
.option('--skip-archive', 'Don\'t create the zip file, leave the build path alone')
.option('-d, --build-destination <path>', 'Target path for the build output, absolute or relative to the plugin root')
.option('-b, --build-version <version>', 'Version for the build output')
.option('-k, --kibana-version <version>', 'Kibana version for the build output')
.action(taskRunner(function (command, files) {
run('build', {
buildDestination: command.buildDestination,
buildVersion: command.buildVersion,
kibanaVersion: command.kibanaVersion,
skipArchive: Boolean(command.skipArchive),
files: files,
});
}));
program
.command('test')
.description('Run the server and browser tests')
.on('--help', docs('test/all'))
.action(taskRunner(function () {
run('testAll');
}));
program
.command('test:browser')
.description('Run the browser tests in a real web browser')
.option('--dev', 'Enable dev mode, keeps the test server running')
.option('-p, --plugins <plugin-ids>', 'Manually specify which plugins\' test bundles to run')
.on('--help', docs('test/browser'))
.action(taskRunner(function (command) {
run('testBrowser', {
dev: Boolean(command.dev),
plugins: command.plugins,
});
}));
program
.command('test:server [files...]')
.description('Run the server tests using mocha')
.on('--help', docs('test/server'))
.action(taskRunner(function (command, files) {
run('testServer', {
files: files
});
}));
program
.command('postinstall')
.action(taskRunner(function () {
run('postinstall');
}));
program
.parse(process.argv);

View file

@ -0,0 +1,49 @@
const resolve = require('path').resolve;
const readFileSync = require('fs').readFileSync;
const configFiles = [ '.kibana-plugin-helpers.json', '.kibana-plugin-helpers.dev.json' ];
const configCache = {};
module.exports = function (root) {
if (!root) root = process.cwd();
if (configCache[root]) {
return configCache[root];
}
// config files to read from, in the order they are merged together
let config = configCache[root] = {};
configFiles.forEach(function (configFile) {
try {
const content = JSON.parse(readFileSync(resolve(root, configFile)));
config = Object.assign(config, content);
} catch (e) {
// rethrow error unless it's complaining about file not existing
if (e.code !== 'ENOENT') {
throw e;
}
}
});
const deprecationMsg = 'has been removed from `@kbn/plugin-helpers`. ' +
'During development your plugin must be located in `../kibana-extra/{pluginName}` ' +
'relative to the Kibana directory to work with this package.\n';
if (config.kibanaRoot) {
throw new Error(
'The `kibanaRoot` config option ' + deprecationMsg
);
}
if (process.env.KIBANA_ROOT) {
throw new Error(
'The `KIBANA_ROOT` environment variable ' + deprecationMsg
);
}
// use resolve to ensure correct resolution of paths
const { includePlugins } = config;
if (includePlugins) config.includePlugins = includePlugins.map(path => resolve(root, path));
return config;
};

View file

@ -0,0 +1,18 @@
const resolve = require('path').resolve;
const readFileSync = require('fs').readFileSync;
function indent(txt, n) {
const space = (new Array(n + 1)).join(' ');
return space + txt.split('\n').join('\n' + space);
}
module.exports = function docs(name) {
const md = readFileSync(resolve(__dirname, '../tasks', name, 'README.md'), 'utf8');
return function () {
console.log('\n Docs:');
console.log('');
console.log(indent(md, 4));
console.log('');
};
};

View file

@ -0,0 +1,10 @@
module.exports = function enableCollectingUnknownOptions(command) {
const origParse = command.parseOptions;
command.allowUnknownOption();
command.parseOptions = function (argv) {
const opts = origParse.call(this, argv);
this.unknownOptions = opts.unknown;
return opts;
};
};

View file

@ -0,0 +1,13 @@
const run = require('./run');
const utils = require('./utils');
module.exports = function () {
console.error(
'running tasks with the default export of @kbn/plugin-helpers is deprecated.' +
'use `require(\'@kbn/plugin-helpers\').run()` instead'
);
return run.apply(this, arguments);
};
Object.assign(module.exports, { run: run }, utils);

View file

@ -0,0 +1,44 @@
const resolve = require('path').resolve;
const statSync = require('fs').statSync;
const readFileSync = require('fs').readFileSync;
const configFile = require('./config_file');
module.exports = function (root) {
if (!root) root = process.cwd();
const pkg = JSON.parse(readFileSync(resolve(root, 'package.json')));
const config = configFile(root);
const buildSourcePatterns = [
'package.json',
'index.js',
'{lib,public,server,webpackShims,translations}/**/*',
];
// add shrinkwrap and lock files, if they exist
['npm-shrinkwrap.json', 'yarn.lock']
.forEach(function (file) {
if (fileExists(resolve(root, file))) {
buildSourcePatterns.push(file);
}
});
return Object.assign({
root: root,
kibanaRoot: resolve(root, '../../kibana'),
serverTestPatterns: ['server/**/__tests__/**/*.js'],
buildSourcePatterns: buildSourcePatterns,
id: pkg.name,
pkg: pkg,
version: pkg.version,
}, config);
};
function fileExists(path) {
try {
const stat = statSync(path);
return stat.isFile();
} catch (e) {
return false;
}
}

View file

@ -0,0 +1,10 @@
const pluginConfig = require('./plugin_config');
const tasks = require('./tasks');
module.exports = function run(name, options) {
const action = tasks[name];
if (!action) throw new Error('Invalid task: "' + name + '"');
const plugin = pluginConfig();
return action(plugin, run, options);
};

View file

@ -0,0 +1,30 @@
/*eslint-env jest*/
const testTask = jest.fn();
const plugin = { id: 'testPlugin' };
jest.mock('./plugin_config', () => () => plugin);
jest.mock('./tasks', () => {
return { testTask };
});
const run = require('./run');
describe('task runner', () => {
beforeEach(() => jest.resetAllMocks());
it('throw given an invalid task', function () {
const invalidTaskName = 'thisisnotavalidtasknameandneverwillbe';
const runner = () => run(invalidTaskName);
expect(runner).toThrow(/invalid task/i);
});
it('runs specified task with plugin and runner', function () {
run('testTask');
const args = testTask.mock.calls[0];
expect(testTask.mock.calls).toHaveLength(1);
expect(args[0]).toBe(plugin);
expect(args[1]).toBe(run);
});
});

View file

@ -0,0 +1,15 @@
const buildTask = require('../tasks/build');
const startTask = require('../tasks/start');
const testAllTask = require('../tasks/test/all');
const testBrowserTask = require('../tasks/test/browser');
const testServerTask = require('../tasks/test/server');
const postinstallTask = require('../tasks/postinstall');
module.exports = {
build: buildTask,
start: startTask,
testAll: testAllTask,
testBrowser: testBrowserTask,
testServer: testServerTask,
postinstall: postinstallTask
};

View file

@ -0,0 +1,38 @@
const resolve = require('path').resolve;
const pluginConfig = require('./plugin_config');
function babelRegister() {
const plugin = pluginConfig();
try {
// add support for moved babel-register source: https://github.com/elastic/kibana/pull/13973
require(resolve(plugin.kibanaRoot, 'src/babel-register'));
} catch (error) {
if (error.code === 'MODULE_NOT_FOUND') {
require(resolve(plugin.kibanaRoot, 'src/optimize/babel/register'));
} else {
throw error;
}
}
}
function resolveKibanaPath(path) {
const plugin = pluginConfig();
return resolve(plugin.kibanaRoot, path);
}
function createToolingLog(level) {
return require(resolveKibanaPath('src/dev')).createToolingLog(level);
}
function readFtrConfigFile(log, path, settingOverrides) {
return require(resolveKibanaPath('src/functional_test_runner')).readConfigFile(log, path, settingOverrides);
}
module.exports = {
babelRegister: babelRegister,
resolveKibanaPath: resolveKibanaPath,
createToolingLog: createToolingLog,
readFtrConfigFile: readFtrConfigFile,
};

View file

@ -0,0 +1,5 @@
const platform = require('os').platform();
module.exports = function winCmd(cmd) {
return /^win/.test(platform) ? cmd + '.cmd' : cmd;
};

View file

@ -0,0 +1,29 @@
{
"name": "@kbn/plugin-helpers",
"private": true,
"version": "9.0.2",
"description": "Just some helpers for kibana plugin devs.",
"main": "lib/index.js",
"bin": {
"plugin-helpers": "bin/plugin-helpers.js"
},
"author": "Spencer Alger <email@spalger.com>",
"license": "Apache-2.0",
"scripts": {
"test": "jest",
"test:dev": "jest --watch"
},
"dependencies": {
"argv-split": "^2.0.1",
"commander": "^2.9.0",
"del": "^2.2.2",
"gulp-rename": "1.2.2",
"gulp-zip": "^4.1.0",
"inquirer": "^1.2.2",
"through2-map": "^3.0.0",
"vinyl-fs": "2.3.1"
},
"devDependencies": {
"jest": "^17.0.3"
}
}

View file

@ -0,0 +1,19 @@
Copies files from the source into a zip archive that can be distributed for
installation into production kibana installs. The archive includes the non-
development npm dependencies and builds itself using raw files in the source
directory so make sure they are clean/up to date. The resulting archive can
be found at:
```
build/{pkg.name}-{pkg.version}.zip
```
If you use the `--build-destination` flag, the resulting build will be found
in that directory.
```
plugin-helpers build --build-destination build/some/child/path
# This will place the resulting build at:
build/some/child/path/{pkg.name}-{pkg.version}.zip
```

View file

@ -0,0 +1,7 @@
module.exports = kibana => new kibana.Plugin({
uiExports: {
hacks: [
'plugins/test_plugin/hack.js'
]
}
});

View file

@ -0,0 +1,16 @@
{
"name": "test_plugin",
"version": "0.0.1",
"kibana": {
"version": "6.0.0"
},
"dependencies": {
},
"devDependencies": {
},
"scripts": {
"start": "node index.js"
}
}

View file

@ -0,0 +1 @@
console.log('this is my hack');

View file

@ -0,0 +1,4 @@
{
"UI-WELCOME_MESSAGE": "Cargando Kibana",
"UI-WELCOME_ERROR": "Kibana no se cargó correctamente. Heck la salida del servidor para obtener más información."
}

View file

@ -0,0 +1,54 @@
const join = require('path').join;
const resolve = require('path').resolve;
const inquirer = require('inquirer');
const createBuild = require('./create_build');
const createPackage = require('./create_package');
module.exports = function (plugin, run, options) {
options = options || {};
let buildVersion = plugin.version;
let kibanaVersion = (plugin.pkg.kibana && plugin.pkg.kibana.version) || plugin.pkg.version;
let buildFiles = plugin.buildSourcePatterns;
let buildTarget = join(plugin.root, 'build');
// allow source files to be overridden
if (options.files && options.files.length) {
buildFiles = options.files;
}
// allow options to override plugin info
if (options.buildDestination) buildTarget = resolve(plugin.root, options.buildDestination);
if (options.buildVersion) buildVersion = options.buildVersion;
if (options.kibanaVersion) kibanaVersion = options.kibanaVersion;
let buildStep;
if (kibanaVersion === 'kibana') {
buildStep = askForKibanaVersion().then(function (customKibanaVersion) {
return createBuild(plugin, buildTarget, buildVersion, customKibanaVersion, buildFiles);
});
} else {
buildStep = createBuild(plugin, buildTarget, buildVersion, kibanaVersion, buildFiles);
}
return buildStep
.then(function () {
if (options.skipArchive) return;
return createPackage(plugin, buildTarget, buildVersion);
})
.catch(function (err) {
console.log('BUILD ACTION FAILED:', err);
});
};
function askForKibanaVersion() {
return inquirer.prompt([
{
type: 'input',
name: 'kibanaVersion',
message: 'What version of Kibana are you building for?'
}
]).then(function (answers) {
return answers.kibanaVersion;
});
}

View file

@ -0,0 +1,90 @@
/*eslint-env jest*/
const resolve = require('path').resolve;
const fs = require('fs');
const del = require('del');
const PLUGIN_FIXTURE = resolve(__dirname, '__fixtures__/test_plugin');
const PLUGIN_BUILD_DIR = resolve(PLUGIN_FIXTURE, 'build');
const PLUGIN = require('../../lib/plugin_config')(PLUGIN_FIXTURE);
const noop = function () {};
describe('build_action', () => {
describe('creating build zip', function () {
const buildAction = require('./build_action');
beforeEach(() => del(PLUGIN_BUILD_DIR));
afterEach(() => del(PLUGIN_BUILD_DIR));
it('creates a zip in the build directory', () => {
return buildAction(PLUGIN).then(() => {
const buildFile = resolve(PLUGIN_BUILD_DIR, PLUGIN.id + '-' + PLUGIN.version + '.zip');
if (!fs.existsSync(buildFile)) {
throw new Error('Build file not found: ' + buildFile);
}
});
});
it('skips zip creation based on flag', function () {
return buildAction(PLUGIN, noop, { skipArchive: true }).then(() => {
const buildFile = resolve(PLUGIN_BUILD_DIR, PLUGIN.id + '-' + PLUGIN.version + '.zip');
if (fs.existsSync(buildFile)) {
throw new Error('Build file not found: ' + buildFile);
}
});
});
});
describe('calling create_build', () => {
let mockBuild;
let buildAction;
beforeEach(() => {
jest.resetModules();
mockBuild = jest.fn(() => Promise.resolve());
jest.mock('./create_build', () => mockBuild);
buildAction = require('./build_action');
});
it('takes optional build version', function () {
const options = {
buildVersion: '1.2.3',
kibanaVersion: '4.5.6',
};
return buildAction(PLUGIN, noop, options).then(() => {
expect(mockBuild.mock.calls).toHaveLength(1);
// eslint-disable-next-line no-unused-vars
const [ plugin, buildTarget, buildVersion, kibanaVersion, files ] = mockBuild.mock.calls[0];
expect(buildVersion).toBe('1.2.3');
expect(kibanaVersion).toBe('4.5.6');
});
});
it('uses default file list without files option', function () {
return buildAction(PLUGIN).then(() => {
expect(mockBuild.mock.calls).toHaveLength(1);
// eslint-disable-next-line no-unused-vars
const [ plugin, buildTarget, buildVersion, kibanaVersion, files ] = mockBuild.mock.calls[0];
PLUGIN.buildSourcePatterns.forEach(file => expect(files).toContain(file));
});
});
it('uses only files passed in', function () {
const options = {
files: [
'index.js',
'LICENSE.txt',
'plugins/**/*',
'{server,public}/**/*'
]
};
return buildAction(PLUGIN, noop, options).then(() => {
expect(mockBuild.mock.calls).toHaveLength(1);
// eslint-disable-next-line no-unused-vars
const [ plugin, buildTarget, buildVersion, kibanaVersion, files ] = mockBuild.mock.calls[0];
options.files.forEach(file => expect(files).toContain(file));
});
});
});
});

View file

@ -0,0 +1,51 @@
const join = require('path').join;
const relative = require('path').relative;
const statSync = require('fs').statSync;
const execFileSync = require('child_process').execFileSync;
const del = require('del');
const vfs = require('vinyl-fs');
const rename = require('gulp-rename');
const rewritePackageJson = require('./rewrite_package_json');
const winCmd = require('../../lib/win_cmd');
module.exports = function createBuild(plugin, buildTarget, buildVersion, kibanaVersion, files) {
const buildSource = plugin.root;
const buildRoot = join(buildTarget, 'kibana', plugin.id);
return del(buildTarget)
.then(function () {
return new Promise(function (resolve, reject) {
vfs
.src(files, { cwd: buildSource, base: buildSource })
// modify the package.json file
.pipe(rewritePackageJson(buildSource, buildVersion, kibanaVersion))
// put all files inside the correct directories
.pipe(rename(function nestFileInDir(path) {
const nonRelativeDirname = path.dirname.replace(/^(\.\.\/?)+/g, '');
path.dirname = join(relative(buildTarget, buildRoot), nonRelativeDirname);
}))
.pipe(vfs.dest(buildTarget))
.on('end', resolve)
.on('error', reject);
});
})
.then(function () {
// install packages in build
const options = {
cwd: buildRoot,
stdio: ['ignore', 'ignore', 'pipe'],
};
try {
// use yarn if yarn lockfile is found in the build
statSync(join(buildRoot, 'yarn.lock'));
execFileSync(winCmd('yarn'), ['install', '--production'], options);
} catch (e) {
// use npm if there is no yarn lockfile in the build
execFileSync(winCmd('npm'), ['install', '--production', '--no-bin-links'], options);
}
});
};

View file

@ -0,0 +1,45 @@
/*eslint-env jest*/
const { resolve } = require('path');
const del = require('del');
const createBuild = require('./create_build');
const PLUGIN_FIXTURE = resolve(__dirname, '__fixtures__/test_plugin');
const PLUGIN = require('../../lib/plugin_config')(PLUGIN_FIXTURE);
const PLUGIN_BUILD_DIR = resolve(PLUGIN_FIXTURE, 'build');
const PLUGIN_BUILD_TARGET = resolve(PLUGIN_BUILD_DIR, 'kibana', PLUGIN.id);
describe('create_build', () => {
beforeEach(() => del(PLUGIN_BUILD_DIR));
afterEach(() => del(PLUGIN_BUILD_DIR));
describe('creating the build', function () {
const buildTarget = resolve(PLUGIN.root, 'build');
const buildVersion = PLUGIN.version;
const kibanaVersion = PLUGIN.version;
const buildFiles = PLUGIN.buildSourcePatterns;
it('removes development properties from package.json', function () {
expect(PLUGIN.pkg.scripts).not.toBeUndefined();
expect(PLUGIN.pkg.devDependencies).not.toBeUndefined();
return createBuild(PLUGIN, buildTarget, buildVersion, kibanaVersion, buildFiles)
.then(() => {
const pkg = require(resolve(PLUGIN_BUILD_TARGET, 'package.json'));
expect(pkg.scripts).toBeUndefined();
expect(pkg.devDependencies).toBeUndefined();
});
});
it('adds build metadata to package.json', function () {
expect(PLUGIN.pkg.build).toBeUndefined();
return createBuild(PLUGIN, buildTarget, buildVersion, kibanaVersion, buildFiles)
.then(() => {
const pkg = require(resolve(PLUGIN_BUILD_TARGET, 'package.json'));
expect(pkg.build).not.toBeUndefined();
expect(pkg.build.git).not.toBeUndefined();
expect(pkg.build.date).not.toBeUndefined();
});
});
});
});

View file

@ -0,0 +1,25 @@
const join = require('path').join;
const relative = require('path').relative;
const del = require('del');
const vfs = require('vinyl-fs');
const zip = require('gulp-zip');
module.exports = function createPackage(plugin, buildTarget, buildVersion) {
const buildId = `${plugin.id}-${buildVersion}`;
const buildRoot = join(buildTarget, 'kibana', plugin.id);
// zip up the package
return new Promise(function (resolve, reject) {
const buildFiles = [relative(buildTarget, buildRoot) + '/**/*'];
vfs.src(buildFiles, { cwd: buildTarget, base: buildTarget })
.pipe(zip(`${buildId}.zip`))
.pipe(vfs.dest(buildTarget))
.on('end', resolve)
.on('error', reject);
})
.then(function () {
// clean up the build path
return del(join(buildTarget, 'kibana'));
});
};

View file

@ -0,0 +1,32 @@
/*eslint-env jest*/
const { resolve } = require('path');
const { statSync } = require('fs');
const del = require('del');
const createBuild = require('./create_build');
const createPackage = require('./create_package');
const PLUGIN_FIXTURE = resolve(__dirname, '__fixtures__/test_plugin');
const PLUGIN = require('../../lib/plugin_config')(PLUGIN_FIXTURE);
const PLUGIN_BUILD_DIR = resolve(PLUGIN_FIXTURE, 'build-custom');
describe('create_build', () => {
const buildVersion = PLUGIN.version;
const kibanaVersion = PLUGIN.version;
const buildFiles = PLUGIN.buildSourcePatterns;
const packageFile = `${PLUGIN.id}-${buildVersion}.zip`;
const doBuild = () => createBuild(PLUGIN, PLUGIN_BUILD_DIR, buildVersion, kibanaVersion, buildFiles);
beforeAll(() => del(PLUGIN_BUILD_DIR).then(doBuild));
afterAll(() => del(PLUGIN_BUILD_DIR));
describe('creating the package', function () {
it('creates zip file in build target path', function () {
return createPackage(PLUGIN, PLUGIN_BUILD_DIR, buildVersion)
.then(() => {
const zipFile = resolve(PLUGIN_BUILD_DIR, packageFile);
const stats = statSync(zipFile);
expect(stats.isFile()).toBeTruthy();
});
});
});
});

View file

@ -0,0 +1,25 @@
const execFileSync = require('child_process').execFileSync;
module.exports = function gitInfo(rootPath) {
try {
const LOG_SEPARATOR = '||';
const commitCount = execFileSync('git', ['rev-list', '--count', 'HEAD'], {
cwd: rootPath,
stdio: ['ignore', 'pipe', 'ignore'],
encoding: 'utf8',
});
const logLine = execFileSync('git', ['log', '--pretty=%h' + LOG_SEPARATOR + '%cD', '-n', '1'], {
cwd: rootPath,
stdio: ['ignore', 'pipe', 'ignore'],
encoding: 'utf8',
}).split(LOG_SEPARATOR);
return {
count: commitCount.trim(),
sha: logLine[0].trim(),
date: logLine[1].trim(),
};
} catch (e) {
return {};
}
};

View file

@ -0,0 +1 @@
module.exports = require('./build_action');

View file

@ -0,0 +1,40 @@
const map = require('through2-map').obj;
const gitInfo = require('./git_info');
module.exports = function rewritePackage(buildSource, buildVersion, kibanaVersion) {
return map(function (file) {
if (file.basename === 'package.json' && file.dirname === buildSource) {
const pkg = JSON.parse(file.contents.toString('utf8'));
// rewrite the target kibana version while the
// file is on it's way to the archive
if (!pkg.kibana) pkg.kibana = {};
pkg.kibana.version = kibanaVersion;
pkg.version = buildVersion;
// append build info
pkg.build = {
git: gitInfo(buildSource),
date: new Date().toString()
};
// remove development properties from the package file
delete pkg.scripts;
delete pkg.devDependencies;
file.contents = toBuffer(JSON.stringify(pkg, null, 2));
}
return file;
});
};
function toBuffer(string) {
if (typeof Buffer.from === 'function') {
return Buffer.from(string, 'utf8');
} else {
// this was deprecated in node v5 in favor
// of Buffer.from(string, encoding)
return new Buffer(string, 'utf8');
}
}

View file

@ -0,0 +1,25 @@
const resolve = require('path').resolve;
const statSync = require('fs').statSync;
module.exports = function (plugin) {
if (
fileExists(resolve(plugin.root, '../kibana/package.json')) &&
!fileExists(resolve(plugin.root, '../../kibana/package.json'))
) {
process.stdout.write(
'\nWARNING: Kibana now requires that plugins must be located in ' +
'`../kibana-extra/{pluginName}` relative to the Kibana folder ' +
'during development. We found a Kibana in `../kibana`, but not in ' +
'`../../kibana`.\n'
);
}
};
function fileExists(path) {
try {
const stat = statSync(path);
return stat.isFile();
} catch (e) {
return false;
}
}

View file

@ -0,0 +1,6 @@
Starts the kibana server with this plugin included. In essence this is the same as running the
following from the root of the kibana install:
```sh
./bin/kibana --dev --plugin-path=../path/to/plugin
```

View file

@ -0,0 +1 @@
module.exports = require('./start_action');

View file

@ -0,0 +1,28 @@
const execFileSync = require('child_process').execFileSync;
const { join } = require('path');
const split = require('argv-split');
module.exports = function (plugin, run, options) {
options = options || {};
const cmd = 'node';
const script = join('scripts', 'kibana.js');
const nodeOptions = split(process.env.NODE_OPTIONS || '');
let args = nodeOptions.concat([script, '--dev', '--plugin-path', plugin.root]);
if (Array.isArray(plugin.includePlugins)) {
plugin.includePlugins.forEach((path) => {
args = args.concat(['--plugin-path', path]);
});
}
if (options.flags) {
args = args.concat(options.flags);
}
execFileSync(cmd, args, {
cwd: plugin.kibanaRoot,
stdio: ['ignore', 1, 2]
});
};

View file

@ -0,0 +1,3 @@
Runs both the server and browser tests, in that order.
This is just a simple caller to both `test/server` and `test/browser`

View file

@ -0,0 +1 @@
module.exports = require('./test_all_action');

View file

@ -0,0 +1,4 @@
module.exports = function testAllAction(plugin, run) {
run('testServer');
run('testBrowser');
};

View file

@ -0,0 +1,60 @@
writing tests
=============
Browser tests are written just like server tests, they are just executed differently.
- place tests near the code they test, in `__tests__` directories throughout
the public directory
- Use the same bdd-style `describe()` and `it()`
api to define the suites and cases of your tests.
```js
describe('some portion of your code', function () {
it('should do this thing', function () {
expect(true).to.be(false);
});
});
```
starting the test runner
========================
Under the covers this command uses the `test:browser` task from kibana. This will execute
your tasks once and exit when complete.
When run with the `--dev` option, the command uses the `test:dev` task from kibana.
This task sets-up a test runner that will watch your code for changes and rebuild your
tests when necessary. You access the test runner through a browser that it starts itself
(via Karma).
If your plugin consists of a number of internal plugins, you may wish to keep the tests
isolated to a specific plugin or plugins, instead of executing all of the tests. To do this,
use `--plugins` and passing the plugins you would like to test. Muliple plugins can be
specified by separating them with commas.
running the tests
=================
Once the test runner has started you a new browser window should be opened and you should
see a message saying "connected". Next to that is a "DEBUG" button. This button will open
an interactive version of your tests that you can refresh, inspects, and otherwise debug
while you write your tests.
focus on the task at hand
=========================
To limit the tests that run you can either:
1. use the ?grep= query string to filter the test cases/suites by name
2. Click the suite title or (play) button next to test output
3. Add `.only` to your `describe()` or `it()` calls:
```js
describe.only('suite name', function () {
// ...
});
```

View file

@ -0,0 +1 @@
module.exports = require('./test_browser_action');

View file

@ -0,0 +1,24 @@
const execFileSync = require('child_process').execFileSync;
const winCmd = require('../../../lib/win_cmd');
module.exports = function testBrowserAction(plugin, run, options) {
options = options || {};
const kbnServerArgs = [
'--kbnServer.plugin-path=' + plugin.root
];
if (options.plugins) {
kbnServerArgs.push('--kbnServer.tests_bundle.pluginId=' + options.plugins);
} else {
kbnServerArgs.push('--kbnServer.tests_bundle.pluginId=' + plugin.id);
}
const task = (options.dev) ? 'test:dev' : 'test:browser';
const args = ['run', task, '--'].concat(kbnServerArgs);
execFileSync(winCmd('npm'), args, {
cwd: plugin.kibanaRoot,
stdio: ['ignore', 1, 2]
});
};

View file

@ -0,0 +1,45 @@
writing tests
=============
Server tests are written just like browser tests, they are just executed differently.
- place tests near the code they test, in `__tests__` directories throughout
the server directory
- Use the same bdd-style `describe()` and `it()` api to define the suites
and cases of your tests.
```js
describe('some portion of your code', function () {
it('should do this thing', function () {
expect(true).to.be(false);
});
});
```
running the tests
=================
Running the server tests is simple, just execute `npm run test:server` in your terminal
and all of the tests in your server will be run.
By default, the runner will look for tests in `server/**/__tests__/**/*.js`. If you'd prefer to
use a different collection of globs and files, you can specify them after the `npm run test:server`
task, like so:
`npm run test:server 'plugins/myplugins/server/__tests__/**/*.js'`
NOTE: quoting the glob pattern is not required, but helps to avoid issues with globbing expansion
in your shell.
focus on the task at hand
=========================
To limit the tests that run add `.only` to your `describe()` or `it()` calls:
```js
describe.only('suite name', function () {
// ...
});
```

View file

@ -0,0 +1 @@
module.exports = require('./test_server_action');

View file

@ -0,0 +1,29 @@
const resolve = require('path').resolve;
const delimiter = require('path').delimiter;
const execFileSync = require('child_process').execFileSync;
const winCmd = require('../../../lib/win_cmd');
module.exports = function (plugin, run, options) {
options = options || {};
const kibanaBins = resolve(plugin.kibanaRoot, 'node_modules/.bin');
const mochaSetupJs = resolve(plugin.kibanaRoot, 'test/mocha_setup.js');
let testPaths = plugin.serverTestPatterns;
// allow server test files to be overridden
if (options.files && options.files.length) {
testPaths = options.files;
}
const fullCmd = resolve(plugin.kibanaRoot, 'node_modules', '.bin', 'mocha');
const cmd = winCmd(fullCmd);
const args = ['--require', mochaSetupJs].concat(testPaths);
const path = `${kibanaBins}${delimiter}${process.env.PATH}`;
execFileSync(cmd, args, {
cwd: plugin.root,
stdio: ['ignore', 1, 2],
env: Object.assign({}, process.env, {
PATH: path
})
});
};

File diff suppressed because it is too large Load diff

View file

@ -9,7 +9,7 @@
export const IGNORE_FILE_GLOBS = [
'.node-version',
'docs/**/*',
'bin/**/*',
'**/bin/**/*',
'**/+([A-Z_]).md',
'**/*.txt',
'Gruntfile.js',

View file

@ -3,4 +3,8 @@
set -e
source "$(dirname $0)/../../src/dev/ci_setup/setup.sh"
cd packages/kbn-plugin-helpers
yarn test
cd -
xvfb-run "$(yarn bin)/grunt" jenkins:unit;