[7.x] [Plugin Generator] Add integration test (#43219) (#45044)

* [Plugin Generator] Add integration test (#43219)

Addresses https://github.com/elastic/kibana/issues/17061

### Features Covered 
 - generating a plugin

#### Then from within the generated plugin's directory
 - running `yarn build`
 - running `yarn test:browser`
 - running `yarn test:server`
 - running `yarn start`
 - running `yarn preinstall`
 - running `yarn lint`
 - running `yarn kbn --help`
 - running `yarn es --help`

* Fixup parse error for intl file
This commit is contained in:
Tre 2019-09-11 07:33:22 -06:00 committed by GitHub
parent fd88942927
commit 86a1f7b0b5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 228 additions and 51 deletions

View file

@ -21,7 +21,7 @@ const { resolve } = require('path');
const { debug } = require('./debug');
const DEFAULT_PLUGIN_PATH = '../../kibana';
const DEFAULT_PLUGIN_PATH = '../..';
/*
* Resolves the path to Kibana, either from default setting or config

View file

@ -0,0 +1,157 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/* eslint-disable no-restricted-syntax */
import { spawn } from 'child_process';
import { resolve } from 'path';
import util from 'util';
import { stat } from 'fs';
import { snakeCase } from 'lodash';
import del from 'del';
import { withProcRunner, ToolingLog } from '@kbn/dev-utils';
import { createEsTestCluster } from '@kbn/test';
import execa from 'execa';
const statP = util.promisify(stat);
const ROOT_DIR = resolve(__dirname, '../../../');
const oneMinute = 60000;
describe(`running the plugin-generator via 'node scripts/generate_plugin.js plugin-name' with default config`, () => {
const pluginName = 'ispec-plugin';
const snakeCased = snakeCase(pluginName);
const generatedPath = resolve(ROOT_DIR, `plugins/${snakeCased}`);
const collect = xs => data => xs.push(data + ''); // Coerce from Buffer to String
beforeAll(() => {
jest.setTimeout(oneMinute * 10);
});
beforeAll(done => {
const create = spawn(process.execPath, ['scripts/generate_plugin.js', pluginName], {
cwd: ROOT_DIR,
});
create.stdout.on('data', function selectDefaults() {
create.stdin.write('\n'); // Generate a plugin with default options.
});
create.on('close', done);
});
afterAll(() => {
del.sync(generatedPath, { force: true });
});
it(`should succeed on creating a plugin in a directory named 'plugins/${snakeCased}`, async () => {
const stats = await statP(generatedPath);
expect(stats.isDirectory()).toBe(true);
});
describe(`then running`, () => {
it(`'yarn test:browser' should exit 0`, async () => {
await execa('yarn', ['test:browser'], { cwd: generatedPath });
});
it(`'yarn test:server' should exit 0`, async () => {
await execa('yarn', ['test:server'], { cwd: generatedPath });
});
it(`'yarn build' should exit 0`, async () => {
await execa('yarn', ['build'], { cwd: generatedPath });
});
it(`'yarn start' should result in the spec plugin being initialized on kibana's stdout`, async () => {
const log = new ToolingLog();
const es = createEsTestCluster({ license: 'basic', log });
await es.start();
await withProcRunner(log, async proc => {
await proc.run('kibana', {
cmd: 'yarn',
args: ['start', '--optimize.enabled=false', '--logging.json=false'],
cwd: generatedPath,
wait: /ispec_plugin.+Status changed from uninitialized to green - Ready/,
});
await proc.stop('kibana');
});
await es.stop();
});
it(`'yarn preinstall' should exit 0`, async () => {
await execa('yarn', ['preinstall'], { cwd: generatedPath });
});
it(`'yarn lint' should exit 0`, async () => {
await execa('yarn', ['lint'], { cwd: generatedPath });
});
it(`'yarn kbn --help' should print out the kbn help msg`, done => {
const helpMsg = `
usage: kbn <command> [<args>]
By default commands are run for Kibana itself, all packages in the 'packages/'
folder and for all plugins in './plugins' and '../kibana-extra'.
Available commands:
bootstrap - Install dependencies and crosslink projects
clean - Remove the node_modules and target directories from all projects.
run - Run script defined in package.json in each package that contains that script.
watch - Runs \`kbn:watch\` script for every project.
Global options:
-e, --exclude Exclude specified project. Can be specified multiple times to exclude multiple projects, e.g. '-e kibana -e @kbn/pm'.
-i, --include Include only specified projects. If left unspecified, it defaults to including all projects.
--oss Do not include the x-pack when running command.
--skip-kibana-plugins Filter all plugins in ./plugins and ../kibana-extra when running command.
`;
const outData = [];
const kbnHelp = spawn('yarn', ['kbn', '--help'], { cwd: generatedPath });
kbnHelp.stdout.on('data', collect(outData));
kbnHelp.on('close', () => {
expect(outData.join('\n')).toContain(helpMsg);
done();
});
});
it(`'yarn es --help' should print out the es help msg`, done => {
const helpMsg = `
usage: es <command> [<args>]
Assists with running Elasticsearch for Kibana development
Available commands:
snapshot - Downloads and run from a nightly snapshot
source - Build and run from source
archive - Install and run from an Elasticsearch tar
build_snapshots - Build and collect ES snapshots
Global options:
--help
`;
const outData = [];
const kbnHelp = spawn('yarn', ['es', '--help'], { cwd: generatedPath });
kbnHelp.stdout.on('data', collect(outData));
kbnHelp.on('close', () => {
expect(outData.join('\n')).toContain(helpMsg);
done();
});
});
});
});

View file

@ -80,7 +80,7 @@ module.exports = function({ name }) {
},
move: {
gitignore: '.gitignore',
eslintrc: '.eslintrc',
'eslintrc.js': '.eslintrc.js',
'package_template.json': 'package.json',
},
data: answers =>

View file

@ -198,6 +198,6 @@ describe('plugin generator sao integration', () => {
it('includes dotfiles', async () => {
const res = await sao.mockPrompt(template);
expect(res.files['.gitignore']).toBeTruthy();
expect(res.files['.eslintrc']).toBeTruthy();
expect(res.files['.eslintrc.js']).toBeTruthy();
});
});

View file

@ -1,5 +1,9 @@
{
"paths": {
"<%= camelCase(name) %>": "./"
}
},
"translations": [
"translations/zh-CN.json"
]
}

View file

@ -1,7 +0,0 @@
---
extends: "@elastic/kibana"
settings:
import/resolver:
'@elastic/eslint-import-resolver-kibana':
rootPackageName: '<%= snakeCase(name) %>'

View file

@ -0,0 +1,24 @@
module.exports = {
root: true,
extends: ['@elastic/eslint-config-kibana', 'plugin:@elastic/eui/recommended'],
settings: {
'import/resolver': {
'@kbn/eslint-import-resolver-kibana': {
rootPackageName: '<%= snakeCase(name) %>',
},
},
},
overrides: [
{
files: ['**/public/**/*'],
settings: {
'import/resolver': {
'@kbn/eslint-import-resolver-kibana': {
forceNode: false,
rootPackageName: '<%= snakeCase(name) %>',
},
},
},
},
]
};

View file

@ -43,39 +43,39 @@ export default function (kibana) {
init(server, options) { // eslint-disable-line no-unused-vars
<%_ if (generateApp) { -%>
const xpackMainPlugin = server.plugins.xpack_main;
if (xpackMainPlugin) {
const featureId = '<%= snakeCase(name) %>';
const xpackMainPlugin = server.plugins.xpack_main;
if (xpackMainPlugin) {
const featureId = '<%= snakeCase(name) %>';
xpackMainPlugin.registerFeature({
id: featureId,
name: i18n.translate('<%= camelCase(name) %>.featureRegistry.featureName', {
defaultMessage: '<%= name %>',
}),
navLinkId: featureId,
icon: 'questionInCircle',
app: [featureId, 'kibana'],
catalogue: [],
privileges: {
all: {
api: [],
savedObject: {
all: [],
read: [],
},
ui: ['show'],
},
read: {
api: [],
savedObject: {
all: [],
read: [],
},
ui: ['show'],
xpackMainPlugin.registerFeature({
id: featureId,
name: i18n.translate('<%= camelCase(name) %>.featureRegistry.featureName', {
defaultMessage: '<%= name %>',
}),
navLinkId: featureId,
icon: 'questionInCircle',
app: [featureId, 'kibana'],
catalogue: [],
privileges: {
all: {
api: [],
savedObject: {
all: [],
read: [],
},
ui: ['show'],
},
});
}
read: {
api: [],
savedObject: {
all: [],
read: [],
},
ui: ['show'],
},
},
});
}
<%_ } -%>
<%_ if (generateApi) { -%>

View file

@ -1 +1 @@
export { Main } from './main';
export { Main } from './main';

View file

@ -31,6 +31,7 @@
var resolve = require('path').resolve;
process.argv.push('--config', resolve(__dirname, '../src/dev/jest/config.integration.js'));
process.argv.push('--runInBand');
require('../src/setup_node_env');
require('../src/dev/jest/cli');

View file

@ -20,6 +20,6 @@
/* eslint-env jest */
/**
* Set the default timeout for the integration test suite to 30 seconds
* Set the default timeout for the integration test suite to 10 minutes
*/
jest.setTimeout(30 * 1000);
jest.setTimeout(10 * 60 * 1000);

View file

@ -52,17 +52,13 @@ export default (kibana) => {
}
} = kbnServer;
const testGlobs = [
'src/legacy/ui/public/**/*.js',
'!src/legacy/ui/public/flot-charts/**/*',
];
const testGlobs = [];
const testingPluginIds = config.get('tests_bundle.pluginId');
if (testingPluginIds) {
testGlobs.push('!src/legacy/ui/public/**/__tests__/**/*');
testingPluginIds.split(',').forEach((pluginId) => {
const plugin = plugins
.find(plugin => plugin.id === pluginId);
const plugin = plugins.find(plugin => plugin.id === pluginId);
if (!plugin) {
throw new Error('Invalid testingPluginId :: unknown plugin ' + pluginId);
@ -78,6 +74,8 @@ export default (kibana) => {
testGlobs.push(`${plugin.publicDir}/**/__tests__/**/*.js`);
});
} else {
// add all since we are not just focused on specific plugins
testGlobs.push('src/legacy/ui/public/**/*.js', '!src/legacy/ui/public/flot-charts/**/*');
// add the modules from all of the apps
for (const app of uiApps) {
modules.add(app.getMainModuleId());