Skip to content

Commit

Permalink
fix #6409: api integration testing setup
Browse files Browse the repository at this point in the history
Signed-off-by: Anton Kosyakov <[email protected]>
  • Loading branch information
akosyakov committed Jan 14, 2020
1 parent c587813 commit 7ab3c06
Show file tree
Hide file tree
Showing 47 changed files with 1,306 additions and 1,805 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ lerna-debug.log
coverage
errorShots
examples/*/src-gen
examples/*/webpack.config.js
examples/*/gen-webpack.config.js
.browser_modules
**/docs/api
package-backup.json
Expand Down
2 changes: 2 additions & 0 deletions .gitpod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ image:
ports:
- port: 3000 # Theia
- port: 3030 # VS Code extension tests
- port: 9229 # Node.js debug port
onOpen: ignore
- port: 9339 # Node.js debug port
onOpen: ignore
- port: 6080
Expand Down
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ cache:
- /tmp/vscode-ripgrep-cache-1.5.7
- dev-packages/application-manager/node_modules
- dev-packages/application-package/node_modules
- dev-packages/cli/node_modules
- dev-packages/electron/node_modules
- examples/api-samples/node_modules
- examples/browser/node_modules
Expand Down
6 changes: 0 additions & 6 deletions NOTICE.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,12 +256,6 @@ vscode-java-debug (0.15.0)

* License: MIT

webdriverio (n/a)

* License: MIT
* Project: http://webdriver.io/
* Source: https://github.com/webdriverio/webdriverio.git

when (3.7.8)

* License: MIT
Expand Down
1 change: 1 addition & 0 deletions configs/mocha.opts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
--require reflect-metadata/Reflect
--reporter spec
--watch-extensions js
--exit
1 change: 1 addition & 0 deletions dev-packages/application-manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"@theia/application-package": "^0.14.0",
"@theia/compression-webpack-plugin": "^3.0.0",
"@types/fs-extra": "^4.0.2",
"@types/webpack": "^4.41.2",
"babel-loader": "^8.0.6",
"circular-dependency-plugin": "^5.0.0",
"copy-webpack-plugin": "^4.5.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,22 +70,22 @@ export class ApplicationPackageManager {
return this.__process.run('webpack', args);
}

async start(args: string[] = []): Promise<void> {
start(args: string[] = []): cp.ChildProcess {
if (this.pck.isElectron()) {
return this.startElectron(args);
}
return this.startBrowser(args);
}

async startElectron(args: string[]): Promise<void> {
startElectron(args: string[]): cp.ChildProcess {
const { mainArgs, options } = this.adjustArgs([this.pck.frontend('electron-main.js'), ...args]);
const electronCli = require.resolve('electron/cli.js', { paths: [this.pck.projectPath] });
this.__process.fork(electronCli, mainArgs, options);
return this.__process.fork(electronCli, mainArgs, options);
}

async startBrowser(args: string[]): Promise<void> {
startBrowser(args: string[]): cp.ChildProcess {
const { mainArgs, options } = this.adjustArgs(args);
this.__process.fork(this.pck.backend('main.js'), mainArgs, options);
return this.__process.fork(this.pck.backend('main.js'), mainArgs, options);
}

private adjustArgs(args: string[], forkOptions: cp.ForkOptions = {}): Readonly<{ mainArgs: string[]; options: cp.ForkOptions }> {
Expand Down
73 changes: 73 additions & 0 deletions dev-packages/application-manager/src/expose-loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/********************************************************************************
* Copyright (C) 2019 TypeFox and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import * as path from 'path';
import * as webpack from 'webpack';
// tslint:disable:no-implicit-dependencies
import { RawSourceMap } from 'source-map';
import { ApplicationPackage } from '@theia/application-package/lib/application-package';

const modulePackages: { dir: string, name?: string }[] = [];
for (const extensionPackage of new ApplicationPackage({ projectPath: process.cwd() }).extensionPackages) {
modulePackages.push({
name: extensionPackage.name,
dir: path.dirname(extensionPackage.raw.installed!.packagePath)
});
}

function exposeModule(modulePackage: { dir: string, name?: string }, resourcePath: string, source: string): string {
if (!modulePackage.name) {
return source;
}
const { dir, name } = path.parse(resourcePath);
let moduleName = path.join(modulePackage.name, dir.substring(modulePackage.dir.length));
if (name !== 'index') {
moduleName = path.join(moduleName, name);
}
if (path.sep !== '/') {
moduleName = moduleName.split(path.sep).join('/');
}
return source + `\nif (!global) global = {};\n(global['theia'] = global['theia'] || {})['${moduleName}'] = this;\n`;
}

export = function (this: webpack.loader.LoaderContext, source: string, sourceMap?: RawSourceMap): string | undefined {
if (this.cacheable) {
this.cacheable();
}

let modulePackage = modulePackages.find(({ dir }) => this.resourcePath.startsWith(dir));
if (modulePackage) {
this.callback(undefined, exposeModule(modulePackage, this.resourcePath, source), sourceMap);
return;
}
const index = this.resourcePath.lastIndexOf('/node_modules');
if (index !== -1) {
const nodeModulesPath = this.resourcePath.substring(0, index + '/node_modules'.length);
let dir = this.resourcePath;
while ((dir = path.dirname(dir)) !== nodeModulesPath) {
try {
const { name } = require(path.join(dir, 'package.json'));
modulePackage = { name, dir };
modulePackages.push(modulePackage);
this.callback(undefined, exposeModule(modulePackage, this.resourcePath, source), sourceMap);
return;
} catch {
/** no-op */
}
}
}
this.callback(undefined, source, sourceMap);
};
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,13 @@ const main = require('@theia/core/lib/node/main');
BackendApplicationConfigProvider.set(${this.prettyStringify(this.pck.props.backend.config)});
const serverModule = require('./server');
const address = main.start(serverModule());
address.then(function (address) {
const serverAddress = main.start(serverModule());
serverAddress.then(function ({ port, address }) {
if (process && process.send) {
process.send(address.port.toString());
process.send({ port, address });
}
});
module.exports = address;
module.exports = serverAddress;
`;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ function load(raw) {
}
function start() {
(window['theia'] = window['theia'] || {}).container = container;
const themeService = ThemeService.get();
themeService.loadUserTheme();
Expand Down Expand Up @@ -317,8 +319,8 @@ app.on('ready', () => {
});
} else {
const cp = fork(mainPath, [], { env: Object.assign({}, process.env) });
cp.on('message', (message) => {
loadMainWindow(message);
cp.on('message', (address) => {
loadMainWindow(address.port);
});
cp.on('error', (error) => {
console.error(error);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,44 @@
********************************************************************************/

import * as paths from 'path';
import * as fs from 'fs-extra';
import { AbstractGenerator } from './abstract-generator';

export class WebpackGenerator extends AbstractGenerator {

async generate(): Promise<void> {
await this.write(this.configPath, this.compileWebpackConfig());
await this.write(this.genConfigPath, this.compileWebpackConfig());
if (await this.shouldGenerateUserWebpackConfig()) {
await this.write(this.configPath, this.compileUserWebpackConfig());
}
}

protected async shouldGenerateUserWebpackConfig(): Promise<boolean> {
if (!(await fs.pathExists(this.configPath))) {
return true;
}
const content = await fs.readFile(this.configPath, 'utf8');
return content.indexOf('gen-webpack') === -1;
}

get configPath(): string {
return this.pck.path('webpack.config.js');
}

get genConfigPath(): string {
return this.pck.path('gen-webpack.config.js');
}

protected resolve(moduleName: string, path: string): string {
return this.pck.resolveModulePath(moduleName, path).split(paths.sep).join('/');
}

protected compileWebpackConfig(): string {
return `// @ts-check
return `/**
* Don't touch this file. It will be renerated by theia build.
* To customize webpack configuration change ${this.configPath}
*/
// @ts-check
const path = require('path');
const webpack = require('webpack');
const yargs = require('yargs');
Expand Down Expand Up @@ -201,4 +221,24 @@ module.exports = {
};`;
}

protected compileUserWebpackConfig(): string {
return `/**
* This file can be edited to customize webpack configuration.
* To reset delete this file and rerun yarn build again.
*/
// @ts-check
const config = require('${this.genConfigPath}');
/**
* Expose bundled modules on window.theia.moduleName namespace, e.g.
* window['theia']['@theia/core/lib/common/uri'].
* Such syntax can be used by external code, for instance, for testing.
config.module.rules.push({
test: /\\.js$/,
loader: require.resolve('@theia/application-manager/lib/expose-loader')
}); */
module.exports = config;`;
}

}
8 changes: 7 additions & 1 deletion dev-packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@
"dependencies": {
"@theia/application-manager": "^0.14.0",
"@theia/application-package": "^0.14.0",
"@types/chai": "^4.2.7",
"@types/mocha": "^5.2.7",
"@types/puppeteer": "^2.0.0",
"chai": "^4.2.0",
"mocha": "^7.0.0",
"puppeteer": "^2.0.0",
"yargs": "^11.1.0"
}
}
}
118 changes: 118 additions & 0 deletions dev-packages/cli/src/run-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/********************************************************************************
* Copyright (C) 2020 TypeFox and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

// tslint:disable:no-any

import * as net from 'net';
import * as puppeteer from 'puppeteer';
const collectFiles = require('mocha/lib/cli/collect-files');

export interface TestOptions {
start: () => Promise<net.AddressInfo>;
launch?: puppeteer.LaunchOptions
files: {
ignore: string[]
extension: string[]
file: string[]
recursive: boolean
sort: boolean
spec: string[]
}
}

export default async function runTest(options: TestOptions): Promise<void> {
const { start, launch, files: fileOptions } = options;
const exit = !(launch && launch.devtools);

// quick check whether test files exist
collectFiles(fileOptions);

const server = await start();
const browser = await puppeteer.launch(launch);

const page = await browser.newPage();
page.on('dialog', dialog => dialog.dismiss());
page.on('pageerror', console.error);

let theiaLoaded = false;
page.exposeFunction('fireDidUnloadTheia', () => theiaLoaded = false);
const preLoad = () => {
if (theiaLoaded) {
return;
}
console.log('loading chai...');
theiaLoaded = true;
page.addScriptTag({ path: require.resolve('chai/chai.js') });
page.evaluate(() =>
window.addEventListener('beforeunload', () => (window as any)['fireDidUnloadTheia']())
);
};
page.on('frameattached', preLoad);
page.on('framenavigated', preLoad);

page.on('load', async () => {
console.log('loading mocha...');
// replace console.log by theia logger for mocha
await page.waitForFunction(() => !!(window as any)['theia']['@theia/core/lib/common/logger'].logger, {
timeout: 30 * 1000
});
await page.addScriptTag({ path: require.resolve('mocha/mocha.js') });
await page.waitForFunction(() => !!(window as any)['chai'] && !!(window as any)['mocha'] && !!(window as any)['theia'].container, { timeout: 30 * 1000 });

console.log('loading Theia...');
await page.evaluate(() => {
const { FrontendApplicationStateService } = (window as any)['theia']['@theia/core/lib/browser/frontend-application-state'];
const { PreferenceService } = (window as any)['theia']['@theia/core/lib/browser/preferences/preference-service'];
const { WorkspaceService } = (window as any)['theia']['@theia/workspace/lib/browser/workspace-service'];

const container = (window as any)['theia'].container;
const frontendApplicationState = container.get(FrontendApplicationStateService);
const preferenceService = container.get(PreferenceService);
const workspaceService = container.get(WorkspaceService);

return Promise.all([
frontendApplicationState.reachedState('ready'),
preferenceService.ready,
workspaceService.roots
]);
});

console.log('loading test files...');
await page.evaluate(() => {
// replace require to load modules from theia namespace
(window as any)['require'] = (moduleName: string) => (window as any)['theia'][moduleName];
mocha.setup({
reporter: 'spec',
ui: 'bdd',
useColors: true
});
});
const files = collectFiles(fileOptions);
for (const file of files) {
await page.addScriptTag({ path: file });
}

console.log('running test files...');
const failures = await page.evaluate(() =>
new Promise<number>(resolve => mocha.run(resolve))
);
if (exit) {
await page.close();
process.exit(failures > 0 ? 1 : 0);
}
});
page.goto(`http://${server.address}:${server.port}`);
}
Loading

0 comments on commit 7ab3c06

Please sign in to comment.