diff --git a/.gitignore b/.gitignore
index 3988427b..37ec9f93 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,8 @@
/src/.baseDir.ts
TODO
test/app.json
+test/client.json
+test/service.json
/docs
cache.dat
/cache
diff --git a/Gruntfile.js b/Gruntfile.js
index 95113f0a..0791f54c 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -124,13 +124,22 @@ module.exports = function (grunt) {
}
});
+ //using the node webpack API: https://webpack.js.org/api/node/
+ //will use Task Error or Warning grunt codes based on webpack results: https://gruntjs.com/exit-codes
grunt.registerTask('webpack', function () {
const done = this.async();
webpack(webpackConfig, (err, stats) => {
- if (err || stats.hasErrors()) {
+ if (err) {
const error = err ? err.message : 'webpack error';
- grunt.log.error(error);
- done(err);
+ if (err.details) {
+ grunt.fail.fatal(err.details, 3);
+ }
+ } else if(stats.hasErrors()) {
+ const info = stats.toJson();
+ grunt.fail.fatal(info.errors, 3);
+ } else if(stats.hasWarnings()) {
+ const info = stats.toJson();
+ grunt.fail.warn(info.warnings, 6);
} else {
grunt.log.ok('webpack task done');
done();
diff --git a/html/client.html b/html/client.html
new file mode 100644
index 00000000..527886f7
--- /dev/null
+++ b/html/client.html
@@ -0,0 +1,23 @@
+
+
+ Service Client
+
+
+
+Service Client
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/html/service.html b/html/service.html
new file mode 100644
index 00000000..37e0e241
--- /dev/null
+++ b/html/service.html
@@ -0,0 +1,24 @@
+
+
+ Service Provider
+
+
+
+Service Provider
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/package.json b/package.json
index ea7a76f5..bf363494 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "hadouken-js-adapter",
- "version": "0.30.2",
+ "version": "0.31.1",
"license": "Apache-2.0",
"repository": "https://github.com/HadoukenIO/js-adapter",
"main": "./out/src/main.js",
diff --git a/src/api/application/application.ts b/src/api/application/application.ts
index eb6960e4..8349f2bb 100644
--- a/src/api/application/application.ts
+++ b/src/api/application/application.ts
@@ -1,4 +1,4 @@
-import { Base, Bare, Reply, RuntimeEvent } from '../base';
+import { EmitterBase, Bare, Reply, RuntimeEvent } from '../base';
import { Identity } from '../../identity';
import { _Window } from '../window/window';
import { Point } from '../system/point';
@@ -79,24 +79,11 @@ export default class ApplicationModule extends Bare {
* execute, show/close an application as well as listen to application events.
* @class
*/
-export class Application extends Base {
+ // @ts-ignore: return types incompatible with EventEmitter (this)
+export class Application extends EmitterBase {
constructor(wire: Transport, public identity: Identity) {
super(wire);
-
- this.on('removeListener', eventType => {
- this.deregisterEventListener(Object.assign({}, this.identity, {
- type: eventType,
- topic: this.topic
- }));
- });
-
- this.on('newListener', eventType => {
- this.registerEventListener(Object.assign({}, this.identity, {
- type: eventType,
- topic: this.topic
- }));
- });
}
protected runtimeEventComparator = (listener: RuntimeEvent): boolean => {
@@ -311,21 +298,22 @@ export class Application extends Base {
}
+// @ts-ignore: return types incompatible with EventEmitter (this)
export interface Application {
- on(type: 'closed', listener: (data: Reply<'application', 'closed'>) => void): this;
- on(type: 'initialized', listener: (data: Reply<'application', 'initialized'>) => void): this;
- on(type: 'connected', listener: (data: Reply<'application', 'connected'>) => void): this;
- on(type: 'crashed', listener: (data: Reply<'application', 'crashed'>) => void): this;
- on(type: 'error', listener: (data: Reply<'application', 'error'>) => void): this;
- on(type: 'not-responding', listener: (data: Reply<'application', 'not-responding'>) => void): this;
- on(type: 'out-of-memory', listener: (data: Reply<'application', 'out-of-memory'>) => void): this;
- on(type: 'responding', listener: (data: Reply<'application', 'responding'>) => void): this;
- on(type: 'started', listener: (data: Reply<'application', 'started'>) => void): this;
- on(type: 'run-requested', listener: (data: Reply<'application', 'run-requested'>) => void): this;
- on(type: 'window-navigation-rejected', listener: (data: NavigationRejectedReply) => void): this;
- on(type: 'window-created', listener: (data: Reply<'application', 'window-created'>) => void): this;
- on(type: 'window-closed', listener: (data: Reply<'application', 'window-closed'>) => void): this;
- on(type: 'tray-icon-clicked', listener: (data: TrayIconClickReply) => void): this;
- on(type: 'removeListener', listener: (eventType: string) => void): this;
- on(type: 'newListener', listener: (eventType: string) => void): this;
+ on(type: 'closed', listener: (data: Reply<'application', 'closed'>) => void): Promise;
+ on(type: 'initialized', listener: (data: Reply<'application', 'initialized'>) => void): Promise;
+ on(type: 'connected', listener: (data: Reply<'application', 'connected'>) => void): Promise;
+ on(type: 'crashed', listener: (data: Reply<'application', 'crashed'>) => void): Promise;
+ on(type: 'error', listener: (data: Reply<'application', 'error'>) => void): Promise;
+ on(type: 'not-responding', listener: (data: Reply<'application', 'not-responding'>) => void): Promise;
+ on(type: 'out-of-memory', listener: (data: Reply<'application', 'out-of-memory'>) => void): Promise;
+ on(type: 'responding', listener: (data: Reply<'application', 'responding'>) => void): Promise;
+ on(type: 'started', listener: (data: Reply<'application', 'started'>) => void): Promise;
+ on(type: 'run-requested', listener: (data: Reply<'application', 'run-requested'>) => void): Promise;
+ on(type: 'window-navigation-rejected', listener: (data: NavigationRejectedReply) => void): Promise;
+ on(type: 'window-created', listener: (data: Reply<'application', 'window-created'>) => void): Promise;
+ on(type: 'window-closed', listener: (data: Reply<'application', 'window-closed'>) => void): Promise;
+ on(type: 'tray-icon-clicked', listener: (data: TrayIconClickReply) => void): Promise;
+ on(type: 'removeListener', listener: (eventType: string) => void): Promise;
+ on(type: 'newListener', listener: (eventType: string) => void): Promise;
}
diff --git a/src/api/base.ts b/src/api/base.ts
index 10e419a5..f3365575 100644
--- a/src/api/base.ts
+++ b/src/api/base.ts
@@ -1,10 +1,11 @@
import Transport, { Message } from '../transport/transport';
import { Identity } from '../identity';
import { EventEmitter } from 'events';
+import { promiseMap } from '../util/promises';
export interface RuntimeEvent extends Identity {
topic: string;
- type: string;
+ type: string|symbol;
}
export class Bare extends EventEmitter {
@@ -32,8 +33,6 @@ export class Bare extends EventEmitter {
}
export class Base extends Bare {
- protected identity: Identity;
-
constructor(wire: Transport) {
super(wire);
wire.registerMessageHandler(this.onmessage.bind(this));
@@ -57,19 +56,20 @@ export class Base extends Bare {
}
}
- protected registerEventListener = (listener: RuntimeEvent): void => {
+ protected registerEventListener = (listener: RuntimeEvent): Promise> => {
const key = createKey(listener);
const refCount = this.wire.topicRefMap.get(key);
if (!refCount) {
this.wire.topicRefMap.set(key, 1);
- this.wire.sendAction('subscribe-to-desktop-event', listener);
+ return this.wire.sendAction('subscribe-to-desktop-event', listener);
} else {
this.wire.topicRefMap.set(key, refCount + 1);
+ return Promise.resolve();
}
}
- protected deregisterEventListener = (listener: RuntimeEvent): void => {
+ protected deregisterEventListener = (listener: RuntimeEvent): Promise> => {
const key = createKey(listener);
const refCount = this.wire.topicRefMap.get(key);
@@ -79,12 +79,104 @@ export class Base extends Bare {
this.wire.topicRefMap.set(key, newRefCount);
if (newRefCount === 0) {
- this.wire.sendAction('unsubscribe-to-desktop-event', listener);
+ return this.wire.sendAction('unsubscribe-to-desktop-event', listener);
}
+ return Promise.resolve();
}
+ }
+}
+
+// @ts-ignore: return types incompatible with EventEmitter (this)
+export class EmitterBase extends Base {
+ protected identity: Identity;
+ // @ts-ignore: return types incompatible with EventEmitter (this)
+ public on(eventType: string, listener: (...args: any[]) => void): Promise {
+ super.on(eventType, listener);
+ return this.registerEventListener(Object.assign({}, this.identity, {
+ type: eventType,
+ topic: this.topic
+ })).then(() => undefined);
+ }
+ // @ts-ignore: return types incompatible with EventEmitter (this)
+ public addListener = this.on;
+ //@ts-ignore: return types incompatible with EventEmitter (this)
+ public once(eventType: string, listener: (...args: any[]) => void): Promise {
+ super.once(eventType, listener);
+ const deregister = () => {
+ this.deregisterEventListener(Object.assign({}, this.identity, {
+ type: eventType,
+ topic: this.topic
+ }));
+ };
+ super.once(eventType, deregister);
+ return this.registerEventListener(Object.assign({}, this.identity, {
+ type: eventType,
+ topic: this.topic
+ })).then(() => undefined);
+ }
+ // @ts-ignore: return types incompatible with EventEmitter (this)
+ public prependListener(eventType: string, listener: (...args: any[]) => void): Promise {
+ super.prependListener(eventType, listener);
+ return this.registerEventListener(Object.assign({}, this.identity, {
+ type: eventType,
+ topic: this.topic
+ })).then(() => undefined);
+ }
+ // @ts-ignore: return types incompatible with EventEmitter (this)
+ public prependOnceListener(eventType: string, listener: (...args: any[]) => void): Promise {
+ super.prependOnceListener(eventType, listener);
+ const deregister = () => {
+ this.deregisterEventListener(Object.assign({}, this.identity, {
+ type: eventType,
+ topic: this.topic
+ }));
+ };
+ super.once(eventType, deregister);
+ return this.registerEventListener(Object.assign({}, this.identity, {
+ type: eventType,
+ topic: this.topic
+ })).then(() => undefined);
+ }
+ // @ts-ignore: return types incompatible with EventEmitter (this)
+ public removeListener(eventType: string, listener: (...args: any[]) => void): Promise {
+ super.removeListener(eventType, listener);
+ return this.deregisterEventListener(Object.assign({}, this.identity, {
+ type: eventType,
+ topic: this.topic
+ })).then(() => undefined);
}
+ protected deregisterAllListeners = (eventType: string|symbol): Promise> => {
+ const runtimeEvent = Object.assign({}, this.identity, {
+ type: eventType,
+ topic: this.topic
+ });
+ const key = createKey(runtimeEvent);
+ const refCount = this.wire.topicRefMap.get(key);
+
+ if (refCount) {
+ this.wire.topicRefMap.delete(key);
+ return this.wire.sendAction('unsubscribe-to-desktop-event', runtimeEvent);
+ } else {
+ return Promise.resolve();
+ }
+ }
+ // @ts-ignore: return types incompatible with EventEmitter (this)
+ public async removeAllListeners(eventType?: string): Promise {
+
+ const removeByEvent = (event: string|symbol): Promise => {
+ super.removeAllListeners(event);
+ return this.deregisterAllListeners(event).then(() => undefined);
+ };
+
+ if (eventType) {
+ return removeByEvent(eventType);
+ } else {
+ const events = this.eventNames();
+ await promiseMap(events, removeByEvent);
+ }
+ }
}
export class Reply implements Identity {
diff --git a/src/api/external-application/external-application.ts b/src/api/external-application/external-application.ts
index b2e63208..ffd37572 100644
--- a/src/api/external-application/external-application.ts
+++ b/src/api/external-application/external-application.ts
@@ -1,4 +1,4 @@
-import { Bare, Base, Reply } from '../base';
+import { Bare, EmitterBase, Reply } from '../base';
import { Identity } from '../../identity';
import Transport from '../../transport/transport';
@@ -23,24 +23,11 @@ export default class ExternalApplicationModule extends Bare {
* well as listen to application events.
* @class
*/
-export class ExternalApplication extends Base {
+// @ts-ignore: return types incompatible with EventEmitter (this)
+export class ExternalApplication extends EmitterBase {
constructor(wire: Transport, public identity: Identity) {
super(wire);
-
- this.on('removeListener', eventType => {
- this.deregisterEventListener(Object.assign({}, this.identity, {
- type: eventType,
- topic : this.topic
- }));
- });
-
- this.on('newListener', eventType => {
- this.registerEventListener(Object.assign({}, this.identity, {
- type: eventType,
- topic : this.topic
- }));
- });
}
/**
@@ -53,9 +40,10 @@ export class ExternalApplication extends Base {
}
}
+// @ts-ignore: return types incompatible with EventEmitter (this)
export interface ExternalApplication {
- on(type: 'connected', listener: (data: Reply<'externalapplication', 'connected'>) => void): this;
- on(type: 'disconnected', listener: (data: Reply<'externalapplication', 'disconnected'>) => void): this;
- on(type: 'removeListener', listener: (eventType: string) => void): this;
- on(type: 'newListener', listener: (eventType: string) => void): this;
+ on(type: 'connected', listener: (data: Reply<'externalapplication', 'connected'>) => void): Promise;
+ on(type: 'disconnected', listener: (data: Reply<'externalapplication', 'disconnected'>) => void): Promise;
+ on(type: 'removeListener', listener: (eventType: string) => void): Promise;
+ on(type: 'newListener', listener: (eventType: string) => void): Promise;
}
diff --git a/src/api/fin.ts b/src/api/fin.ts
index a80b4591..3738075d 100644
--- a/src/api/fin.ts
+++ b/src/api/fin.ts
@@ -9,6 +9,7 @@ import Clipbpard from './clipboard/clipboard';
import ExternalApplication from './external-application/external-application';
import _FrameModule from './frame/frame';
import Plugin from './plugin/plugin';
+import { Service } from './services';
export default class Fin extends Bare {
public System: System;
@@ -20,6 +21,7 @@ export default class Fin extends Bare {
public ExternalApplication: ExternalApplication;
public Frame: _FrameModule;
public Plugin: Plugin;
+ public Service: Service;
constructor(wire: Transport, public token: string) {
super(wire);
@@ -32,6 +34,7 @@ export default class Fin extends Bare {
this.ExternalApplication = new ExternalApplication(wire);
this.Frame = new _FrameModule(wire);
this.Plugin = new Plugin(wire);
+ this.Service = new Service(wire);
//Handle disconnect events
wire.on('disconnected', () => {
diff --git a/src/api/frame/frame.ts b/src/api/frame/frame.ts
index c0d6047e..2017f823 100644
--- a/src/api/frame/frame.ts
+++ b/src/api/frame/frame.ts
@@ -1,4 +1,4 @@
-import { Bare, Base } from '../base';
+import { Bare, EmitterBase } from '../base';
import { Identity } from '../../identity';
import Transport from '../../transport/transport';
@@ -38,25 +38,12 @@ export default class _FrameModule extends Bare {
* @class
* @alias Frame
*/
+// @ts-ignore: return types incompatible with EventEmitter (this)
// tslint:disable-next-line
-export class _Frame extends Base {
+export class _Frame extends EmitterBase {
constructor(wire: Transport, public identity: Identity) {
super(wire);
-
- this.on('removeListener', eventType => {
- this.deregisterEventListener(Object.assign({}, this.identity, {
- type: eventType,
- topic : this.topic
- }));
- });
-
- this.on('newListener', eventType => {
- this.registerEventListener(Object.assign({}, this.identity, {
- type: eventType,
- topic : this.topic
- }));
- });
}
/**
@@ -80,8 +67,9 @@ export class _Frame extends Base {
}
+// @ts-ignore: return types incompatible with EventEmitter (this)
// tslint:disable-next-line
export interface _Frame {
- on(type: 'removeListener', listener: (eventType: string) => void): this;
- on(type: 'newListener', listener: (eventType: string) => void): this;
+ on(type: 'connected', listener: (eventType: string) => void): Promise;
+ on(type: 'disconnected', listener: (eventType: string) => void): Promise;
}
diff --git a/src/api/plugin/plugin.ts b/src/api/plugin/plugin.ts
index c30f3cd9..c145e9e7 100644
--- a/src/api/plugin/plugin.ts
+++ b/src/api/plugin/plugin.ts
@@ -2,11 +2,6 @@ import { Base } from '../base';
import Transport from '../../transport/transport';
import { notImplementedEnvErrorMsg } from '../../environment/environment';
-export interface PluginBare {
- name: string;
- version: string;
-}
-
/**
* The Plugin API allows importing OpenFin plugins
* @namespace
@@ -32,13 +27,11 @@ export default class Plugin extends Base {
* **Important**: If you set HTTP Content-Security-Policy's `script-src` directive
* you must allow `unsafe-inline` for `blob:` for this API to work.
*
- * @param {Object} plugin - Plugin to import. Specified plugin must be listed in app's manifest.
- * @param {string} plugin.name - plugin name
- * @param {string} plugin.version - plugin version
+ * @param {string} name - Plugin to import. Specified plugin must be listed in app's manifest.
* @return {Promise}
* @tutorial Plugin.import
*/
- public async import(plugin: PluginBare): Promise {
+ public async import(name: string): Promise {
if (!this.isOpenFinEnvironment()) {
throw new Error(notImplementedEnvErrorMsg);
}
@@ -47,10 +40,10 @@ export default class Plugin extends Base {
throw new Error(this.noEsmSupportErrorMsg);
}
- const { payload } = await this.wire.sendAction('get-plugin-module', { plugin });
- const { data: { _content } } = payload;
+ const { payload } = await this.wire.sendAction('get-plugin-module', name);
+ const { data: content } = payload;
- return this.importModule(_content);
+ return this.importModule(content);
}
// ESM is supported in OF v9+
diff --git a/src/api/services/channel.ts b/src/api/services/channel.ts
new file mode 100644
index 00000000..5d9fb95f
--- /dev/null
+++ b/src/api/services/channel.ts
@@ -0,0 +1,107 @@
+import { Identity } from '../../identity';
+import Transport, { Message } from '../../transport/transport';
+
+const idOrResult = (func: (...args: any[]) => any) => (...args: any[] ) => {
+ const res = func(...args);
+ return res === undefined ? args[1] : res;
+};
+
+//tslint:disable-next-line
+export interface ServiceIdentity extends Identity {}
+
+export type Action = (() => any)
+ | ((payload: any) => any)
+ | ((payload: any, id: ServiceIdentity) => any);
+export type Middleware = (() => any)
+ | ((action: string) => any)
+ | ((action: string, payload: any) => any)
+ | ((action: string, payload: any, id: ServiceIdentity) => any);
+
+export interface ServiceMessagePayload extends Identity {
+ action: string;
+ payload: any;
+}
+
+export class ServiceChannel {
+ protected subscriptions: any;
+ public defaultAction: (action?: string, payload?: any, senderIdentity?: ServiceIdentity) => any;
+ private preAction: (...args: any[]) => any;
+ private postAction: (...args: any[]) => any;
+ private errorMiddleware: (...args: any[]) => any;
+ private defaultSet: boolean;
+ protected send: (to: Identity, action: string, payload: any) => Promise>;
+
+ constructor (send: Transport['sendAction']) {
+ this.defaultSet = false;
+ this.subscriptions = new Map any>();
+ this.defaultAction = () => {
+ throw new Error('No action registered');
+ };
+ this.send = async (to: Identity, action: string, payload: any) => {
+ const raw = await send('send-service-message', { ...to, action, payload }).catch(reason => {
+ throw new Error(reason.message);
+ });
+ return raw.payload.data.result;
+ };
+ }
+
+ public async processAction(action: string, payload: any, senderIdentity: ServiceIdentity) {
+ try {
+ const mainAction = this.subscriptions.has(action)
+ ? this.subscriptions.get(action)
+ : (payload: any, id: ServiceIdentity) => this.defaultAction(action, payload, id);
+ const preActionProcessed = this.preAction ? await this.preAction(action, payload, senderIdentity) : payload;
+ const actionProcessed = await mainAction(preActionProcessed, senderIdentity);
+ return this.postAction
+ ? await this.postAction(action, actionProcessed, senderIdentity)
+ : actionProcessed;
+ } catch (e) {
+ if (this.errorMiddleware) {
+ return this.errorMiddleware(action, e, senderIdentity);
+ } throw e;
+ }
+ }
+
+ public beforeAction(func: Action) {
+ if (this.preAction) {
+ throw new Error('Already registered beforeAction middleware');
+ }
+ this.preAction = idOrResult(func);
+ }
+
+ public onError(func: (e: any, action: string, id: Identity) => any) {
+ if (this.errorMiddleware) {
+ throw new Error('Already registered error middleware');
+ }
+ this.errorMiddleware = func;
+ }
+
+ public afterAction(func: Action) {
+ if (this.postAction) {
+ throw new Error('Already registered afterAction middleware');
+ }
+ this.postAction = idOrResult(func);
+ }
+
+ public remove(action: string): void {
+ this.subscriptions.delete(action);
+ }
+
+ public setDefaultAction(func: (action?: string, payload?: any, senderIdentity?: ServiceIdentity) => any): void {
+ if (this.defaultSet) {
+ throw new Error('default action can only be set once');
+ } else {
+ this.defaultAction = func;
+ this.defaultSet = true;
+ }
+ }
+
+ public register(topic: string, listener: Action) {
+ if (this.subscriptions.has(topic)) {
+ throw new Error(`Subscription already registered for action: ${topic}. Unsubscribe before adding new subscription`);
+ } else {
+ this.subscriptions.set(topic, listener);
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/api/services/client.ts b/src/api/services/client.ts
new file mode 100644
index 00000000..ba05993c
--- /dev/null
+++ b/src/api/services/client.ts
@@ -0,0 +1,14 @@
+import { ServiceChannel, ServiceIdentity } from './channel';
+import Transport from '../../transport/transport';
+
+export class Client extends ServiceChannel {
+ public onServiceDisconnect: (f: () => void) => void;
+ constructor(private identity: ServiceIdentity, send: Transport['sendAction']) {
+ super(send);
+ }
+
+ public async dispatch(action: string, payload?: any): Promise {
+ return this.send(this.identity, action, payload);
+ }
+
+}
diff --git a/src/api/services/index.ts b/src/api/services/index.ts
new file mode 100644
index 00000000..054b3ae7
--- /dev/null
+++ b/src/api/services/index.ts
@@ -0,0 +1,107 @@
+import { Client } from './client';
+import { Identity } from '../../identity';
+import { Provider } from './provider';
+import { Base } from '../base';
+import Transport, { Message, Payload } from '../../transport/transport';
+
+export interface Options {
+ wait?: boolean;
+ uuid: string;
+ payload?: any;
+}
+
+export interface ServicePayload {
+ payload: Payload;
+}
+export interface ServiceMessage extends Message {
+ senderIdentity: Identity;
+ ackToSender: any;
+ serviceIdentity: Identity;
+ connectAction: boolean;
+}
+
+export class Service extends Base {
+ private serviceMap: Map;
+ constructor(wire: Transport) {
+ super(wire);
+ this.serviceMap = new Map();
+ wire.registerMessageHandler(this.onmessage.bind(this));
+ }
+
+ public async onServiceConnect(identity: Identity, listener: EventListener): Promise {
+ this.registerEventListener({
+ topic: 'service',
+ type: 'connected',
+ ...identity
+ });
+ this.on('connected', listener);
+ }
+
+ public async connect(options: Options): Promise {
+ try {
+ const { payload: { data: serviceIdentity } } = await this.wire.sendAction('send-service-message', Object.assign({
+ connectAction: true,
+ wait: true
+ }, options));
+ const channel = new Client(serviceIdentity, this.wire.sendAction.bind(this.wire));
+ channel.onServiceDisconnect = (listener: () => void) => {
+ this.registerEventListener({
+ topic: 'service',
+ type: 'disconnected',
+ ...serviceIdentity
+ });
+ this.on('disconnected', listener);
+ };
+ this.serviceMap.set(serviceIdentity.uuid, channel);
+ return channel;
+ } catch (e) {
+ throw new Error(e.message);
+ }
+ }
+
+ public async register(): Promise {
+ const { payload: { data: serviceIdentity } } = await this.wire.sendAction('register-service', {});
+ const channel = new Provider(this.wire.sendAction.bind(this.wire));
+ this.serviceMap.set(serviceIdentity.uuid, channel);
+ return channel;
+ }
+ public onmessage = (msg: ServiceMessage) => {
+ if (msg.action === 'process-service-action') {
+ this.processServiceMessage(msg);
+ return true;
+ }
+ return false;
+ }
+ private async processServiceMessage (msg: ServiceMessage) {
+ const { senderIdentity, serviceIdentity, action, ackToSender, payload, connectAction} = msg.payload;
+ const bus = this.serviceMap.get(serviceIdentity.uuid);
+ try {
+ let res;
+ if (!bus) {
+ return;
+ }
+ if (connectAction) {
+ if (!(bus instanceof Provider)) {
+ throw Error('Cannot connect to a plugin');
+ }
+ res = await bus.processConnection(senderIdentity, payload);
+ } else {
+ res = await bus.processAction(action, payload, senderIdentity);
+ }
+ ackToSender.payload.payload = ackToSender.payload.payload || {};
+ ackToSender.payload.payload.result = res;
+ this.wire.sendRaw(ackToSender);
+ } catch (e) {
+ ackToSender.success = false;
+ ackToSender.reason = e.message;
+ this.wire.sendRaw(ackToSender);
+ }
+ }
+
+}
+
+interface PluginSubscribeSuccess {
+ uuid: string;
+ name: string;
+ serviceName: string;
+}
diff --git a/src/api/services/provider.ts b/src/api/services/provider.ts
new file mode 100644
index 00000000..4aade956
--- /dev/null
+++ b/src/api/services/provider.ts
@@ -0,0 +1,33 @@
+import { ServiceChannel, ServiceIdentity } from './channel';
+import Transport from '../../transport/transport';
+
+export type ConnectionListener = (adapterIdentity: ServiceIdentity, connectionMessage?: any) => any;
+
+export class Provider extends ServiceChannel {
+ private connectListener: ConnectionListener;
+ private connections: ServiceIdentity[];
+
+ constructor(send: Transport['sendAction']) {
+ super(send);
+ this.connectListener = () => undefined;
+ this.connections = [];
+ }
+
+ public dispatch(to: ServiceIdentity, action: string, payload: any): Promise {
+ return this.send(to, action, payload);
+ }
+
+ public async processConnection(senderId: ServiceIdentity, payload: any) {
+ this.connections.push(senderId);
+ return this.connectListener(senderId, payload);
+ }
+
+ public publish(action: string, payload: any): Promise[] {
+ return this.connections.map(to => this.send(to, action, payload));
+ }
+
+ public onConnection(listener: ConnectionListener): void {
+ this.connectListener = listener;
+ }
+
+}
\ No newline at end of file
diff --git a/src/api/system/system.ts b/src/api/system/system.ts
index 5757c835..25a7894e 100644
--- a/src/api/system/system.ts
+++ b/src/api/system/system.ts
@@ -1,4 +1,4 @@
-import { Base } from '../base';
+import { EmitterBase } from '../base';
import { ApplicationInfo } from './application';
import { WindowInfo } from './window';
import { Identity } from '../../identity';
@@ -164,25 +164,11 @@ import { RuntimeError, NotSupportedError } from '../../transport/transport-error
* clearing the cache and exiting the runtime.
* @namespace
*/
-export default class System extends Base {
+// @ts-ignore: return types incompatible with EventEmitter (this)
+export default class System extends EmitterBase {
constructor(wire: Transport) {
super(wire);
-
- this.on('removeListener', (eventType: string) => {
- this.deregisterEventListener(Object.assign({}, this.identity, {
- type: eventType,
- topic: this.topic
- }));
- });
-
- this.on('newListener', (eventType: string) => {
- this.registerEventListener(Object.assign({}, this.identity, {
- type: eventType,
- topic: this.topic
- }));
- });
-
}
/**
diff --git a/src/api/window/window.ts b/src/api/window/window.ts
index 09801ab9..49eee221 100644
--- a/src/api/window/window.ts
+++ b/src/api/window/window.ts
@@ -1,4 +1,4 @@
-import { Bare, Base, RuntimeEvent } from '../base';
+import { Bare, EmitterBase, RuntimeEvent } from '../base';
import { Identity } from '../../identity';
import Bounds from './bounds';
import BoundsChangedReply from './bounds-changed';
@@ -161,8 +161,9 @@ this animation onto the end of the animation queue.
* @alias Window
*/
// The window.Window name is taken
+// @ts-ignore: return types incompatible with EventEmitter (this)
// tslint:disable-next-line
-export class _Window extends Base {
+export class _Window extends EmitterBase {
/**
* Raised when a window within this application requires credentials from the user.
*
@@ -541,20 +542,6 @@ export class _Window extends Base {
*/
constructor(wire: Transport, public identity: Identity) {
super(wire);
-
- this.on('removeListener', eventType => {
- this.deregisterEventListener(Object.assign({}, this.identity, {
- type: eventType,
- topic: this.topic
- }));
- });
-
- this.on('newListener', eventType => {
- this.registerEventListener(Object.assign({}, this.identity, {
- type: eventType,
- topic: this.topic
- }));
- });
}
protected runtimeEventComparator = (listener: RuntimeEvent): boolean => {
@@ -1054,15 +1041,15 @@ export class _Window extends Base {
}
}
-
+// @ts-ignore: "on" return types incompatible with EventEmitter (this)
// tslint:disable-next-line
export interface _Window {
- on(type: 'focused', listener: Function): this;
- on(type: 'initialized', listener: Function): this;
- on(type: 'bounds-changed', listener: (data: BoundsChangedReply) => void): this;
- on(type: 'hidden', listener: Function): this;
- on(type: 'removeListener', listener: (eventType: string) => void): this;
- on(type: 'newListener', listener: (eventType: string) => void): this;
- on(type: 'closed', listener: (eventType: CloseEventShape) => void): this;
- on(type: 'fire-constructor-callback', listener: Function): this;
+ on(type: 'focused', listener: Function): Promise;
+ on(type: 'initialized', listener: Function): Promise;
+ on(type: 'bounds-changed', listener: (data: BoundsChangedReply) => void): Promise;
+ on(type: 'hidden', listener: Function): Promise;
+ on(type: 'removeListener', listener: (eventType: string | symbol) => void): Promise;
+ on(type: 'newListener', listener: (eventType: string | symbol) => void): Promise;
+ on(type: 'closed', listener: (eventType: CloseEventShape) => void): Promise;
+ on(type: 'fire-constructor-callback', listener: Function): Promise;
}
diff --git a/src/environment/openfin-env.ts b/src/environment/openfin-env.ts
index 7a8c3fef..77c6a790 100644
--- a/src/environment/openfin-env.ts
+++ b/src/environment/openfin-env.ts
@@ -39,6 +39,10 @@ export default class OpenFinEnvironment implements Environment {
return reject(new Error('Trying to create a window that already exists'));
}
+ // we should register the window name with the core asap to prevent
+ // multiple windows claiming the same uuid-name combo
+ fin.__internal_.registerWindowName(opt.uuid, opt.name);
+
if (opt.url !== ABOUT_BLANK) {
opt.url = this.resolveUrl(opt.url);
}
diff --git a/src/launcher/nix-launch.ts b/src/launcher/nix-launch.ts
index 95b4d6fb..8dc941ea 100644
--- a/src/launcher/nix-launch.ts
+++ b/src/launcher/nix-launch.ts
@@ -2,7 +2,8 @@ import * as fs from 'fs';
import * as path from 'path';
import { ChildProcess, spawn } from 'child_process';
import { ConfigWithRuntime } from '../transport/wire';
-import { promisify, resolveRuntimeVersion, rmDir, downloadFile, unzip, resolveDir, exists } from './util';
+import { promisify } from '../util/promises';
+import { resolveRuntimeVersion, rmDir, downloadFile, unzip, resolveDir, exists } from './util';
const mkdir = promisify(fs.mkdir);
diff --git a/src/launcher/util.ts b/src/launcher/util.ts
index d4e40c9c..900e678d 100644
--- a/src/launcher/util.ts
+++ b/src/launcher/util.ts
@@ -2,12 +2,7 @@ import * as path from 'path';
import * as https from 'https';
import * as fs from 'fs';
import { exec } from 'child_process';
-
-export function promisify(func: Function): (...args: any[]) => Promise {
- return (...args: any[]) => new Promise((resolve, reject) => {
- func(...args, (err: Error, val: any) => err ? reject(err) : resolve(val));
- });
-}
+import { promisify, promiseMap } from '../util/promises';
const stat = promisify(fs.stat);
export async function exists(path: string): Promise {
@@ -149,20 +144,3 @@ export async function resolveDir(base: string, paths: string[]): Promise
}
}, Promise.resolve(base));
}
-
-export async function promiseMap(arr: T[], asyncF: (x: T, i: number, r: T[]) => Promise): Promise {
- return Promise.all(arr.map(asyncF));
-}
-
-export type asyncF = (...args: any[]) => Promise;
-export async function serial(arr: asyncF[]): Promise {
- const ret: T[] = [];
- for (const func of arr) {
- const next = await func();
- ret.push(next);
- }
- return ret;
-}
-export async function promiseMapSerial(arr: any[], func: asyncF): Promise {
- return serial(arr.map((value, index, array) => () => func(value, index, array)));
-}
diff --git a/src/transport/transport.ts b/src/transport/transport.ts
index f8e2f6d1..68e8d8b8 100644
--- a/src/transport/transport.ts
+++ b/src/transport/transport.ts
@@ -20,9 +20,7 @@ import {
declare var fin: any;
-export interface MessageHandler {
- (data: Function): boolean;
-}
+export type MessageHandler = (data: any) => boolean;
class Transport extends EventEmitter {
protected wireListeners: Map = new Map();
@@ -32,11 +30,13 @@ class Transport extends EventEmitter {
protected wire: Wire;
public environment: Environment;
public topicRefMap: Map = new Map();
+ public sendRaw: Wire['send'];
constructor(wireType: WireConstructor, environment: Environment) {
super();
this.wire = new wireType(this.onmessage.bind(this));
this.environment = environment;
+ this.sendRaw = this.wire.send.bind(this.wire);
this.registerMessageHandler(this.handleMessage.bind(this));
this.wire.on('disconnected', () => {
diff --git a/src/util/normalize-config.ts b/src/util/normalize-config.ts
index 1baad319..57f7f1db 100644
--- a/src/util/normalize-config.ts
+++ b/src/util/normalize-config.ts
@@ -2,7 +2,7 @@ import { ConnectConfig, isExternalConfig, InternalConnectConfig, ExternalConfig,
import { Url, parse } from 'url';
import { IncomingMessage } from 'http';
import * as fs from 'fs';
-import { promisify } from '../launcher/util';
+import { promisify } from '../util/promises';
async function readLocalConfig(location: string): Promise {
const txt = await promisify(fs.readFile)(location);
@@ -59,4 +59,4 @@ export async function validateConfig(config: ConnectConfig) {
} else {
throw new Error('Invalid Config');
}
-}
\ No newline at end of file
+}
diff --git a/src/util/promises.ts b/src/util/promises.ts
new file mode 100644
index 00000000..6693d479
--- /dev/null
+++ b/src/util/promises.ts
@@ -0,0 +1,22 @@
+export function promisify(func: Function): (...args: any[]) => Promise {
+ return (...args: any[]) => new Promise((resolve, reject) => {
+ func(...args, (err: Error, val: any) => err ? reject(err) : resolve(val));
+ });
+}
+
+export async function promiseMap(arr: T[], asyncF: (x: T, i: number, r: T[]) => Promise): Promise {
+ return Promise.all(arr.map(asyncF));
+}
+
+export type asyncF = (...args: any[]) => Promise;
+export async function serial(arr: asyncF[]): Promise {
+ const ret: T[] = [];
+ for (const func of arr) {
+ const next = await func();
+ ret.push(next);
+ }
+ return ret;
+}
+export async function promiseMapSerial(arr: any[], func: asyncF): Promise {
+ return serial(arr.map((value, index, array) => () => func(value, index, array)));
+}
diff --git a/test/application.test.ts b/test/application.test.ts
index dd28cf55..571ea1cc 100644
--- a/test/application.test.ts
+++ b/test/application.test.ts
@@ -1,5 +1,6 @@
import { conn } from './connect';
import { Fin, Application, connect as rawConnect } from '../src/main';
+import { cleanOpenRuntimes } from './multi-runtime-utils';
import * as assert from 'assert';
import * as path from 'path';
@@ -11,10 +12,10 @@ describe('Application.', function() {
this.timeout(30000);
let counter = 0;
- before(() => conn().then((a: Fin) => {
-
- fin = a;
- }));
+ before(async () => {
+ await cleanOpenRuntimes();
+ fin = await conn();
+ });
beforeEach(async () => {
testApp = await fin.Application.create({
diff --git a/test/clipboard.test.ts b/test/clipboard.test.ts
index 17b0a6f1..bfb55948 100644
--- a/test/clipboard.test.ts
+++ b/test/clipboard.test.ts
@@ -1,6 +1,7 @@
import { conn } from './connect';
import * as assert from 'assert';
import { Fin } from '../src/main';
+import { cleanOpenRuntimes } from './multi-runtime-utils';
describe('Clipboard.', () => {
let fin: Fin;
@@ -17,8 +18,9 @@ describe('Clipboard.', () => {
}
};
- before(() => {
- return conn().then((a: Fin) => fin = a);
+ before(async () => {
+ await cleanOpenRuntimes();
+ fin = await conn();
});
describe('writeText()', () => {
diff --git a/test/connect.test.ts b/test/connect.test.ts
index c108cd01..c3937ae1 100644
--- a/test/connect.test.ts
+++ b/test/connect.test.ts
@@ -1,11 +1,13 @@
import { conn } from './connect';
import * as assert from 'assert';
import { connect as rawConnect, Fin } from '../src/main';
+import { cleanOpenRuntimes } from './multi-runtime-utils';
describe('connect()', () => {
let fin: Fin;
- before(() => {
- return conn().then((a: Fin) => fin = a);
+ before(async () => {
+ await cleanOpenRuntimes();
+ fin = await conn();
});
it('authentication', () => {
assert(fin.System !== undefined);
diff --git a/test/connect.ts b/test/connect.ts
index 10783d6a..c94babaf 100644
--- a/test/connect.ts
+++ b/test/connect.ts
@@ -1,5 +1,4 @@
import { connect, Fin } from '../src/main';
-import { kill } from './multi-runtime-utils';
const MAX_TRY_NUMBER = 5;
let c: Promise;
@@ -24,11 +23,3 @@ export function conn() {
return c;
}
-
-export async function clean() {
- if (c) {
- const f = await c;
- kill(f);
- c = null;
- }
-}
diff --git a/test/event-emitter.test.ts b/test/event-emitter.test.ts
new file mode 100644
index 00000000..dc052189
--- /dev/null
+++ b/test/event-emitter.test.ts
@@ -0,0 +1,83 @@
+import * as assert from 'assert';
+import { conn } from './connect';
+import { Fin } from '../src/main';
+import * as sinon from 'sinon';
+import { cleanOpenRuntimes } from './multi-runtime-utils';
+
+describe ('Event Emitter Methods', () => {
+ let fin: Fin;
+ let app: any;
+ let win: any;
+ const appConfigTemplate = {
+ name: 'adapter-test-app',
+ url: 'about:blank',
+ uuid: 'adapter-test-app',
+ autoShow: true,
+ saveWindowState: false,
+ accelerator: {
+ devtools: true
+ }
+ };
+
+ before(async () => {
+ await cleanOpenRuntimes();
+ fin = await conn();
+ });
+
+ beforeEach(async () => {
+ app = await fin.Application.create(appConfigTemplate);
+ await app.run();
+ win = await app.getWindow();
+ });
+
+ afterEach(async() => {
+ await app.close();
+ });
+
+ describe('once', () => {
+ it('should only get called once then removed', async () => {
+ const spy = sinon.spy();
+ await win.once('bounds-changed', spy);
+ await win.moveBy(1, 1);
+ await win.moveBy(1, 1);
+ assert(spy.calledOnce);
+ });
+ });
+
+ describe('removeAllListeners', () => {
+ it('should remove listeners for a given event', async () => {
+ const boundsSpy = sinon.spy();
+ const closedSpy = sinon.spy();
+ await win.addListener('bounds-changed', boundsSpy);
+ await win.addListener('closed', closedSpy);
+ await win.moveBy(1, 1);
+ await win.removeAllListeners('bounds-changed');
+ await win.moveBy(1, 1);
+ const eventNames = win.eventNames();
+ await win.close();
+ assert(eventNames.length === 1, `Expected ${eventNames} to be closed and only closed`);
+ assert(boundsSpy.calledOnce);
+ assert(closedSpy.calledOnce);
+ });
+
+ it('should remove listeners for all events', async () => {
+ const boundsSpy = sinon.spy();
+ const closedSpy = sinon.spy();
+ await win.addListener('bounds-changed', boundsSpy);
+ await win.on('closed', closedSpy);
+ await win.moveBy(1, 1);
+ await win.removeAllListeners();
+ const noEvents = win.eventNames();
+ await win.moveBy(1, 1);
+ await win.on('bounds-changed', boundsSpy);
+ const eventNames = win.eventNames();
+ await win.moveBy(1, 1);
+ await win.close();
+ assert(boundsSpy.calledTwice);
+ assert(closedSpy.notCalled);
+ assert(eventNames.length === 1, `Expected ${eventNames} to be bounds-changed and only bounds-changed`);
+ assert(noEvents.length === 0, `Expected ${eventNames} event to not exist`);
+ });
+ });
+
+});
diff --git a/test/external-application.test.ts b/test/external-application.test.ts
index 3be937b3..56c2e8a2 100644
--- a/test/external-application.test.ts
+++ b/test/external-application.test.ts
@@ -1,10 +1,14 @@
import { conn } from './connect';
import { Fin } from '../src/main';
import * as assert from 'assert';
+import { cleanOpenRuntimes } from './multi-runtime-utils';
describe('ExternalApplication.', () => {
let fin: Fin;
- before(() => conn().then((a: Fin) => fin = a));
+ before(async () => {
+ await cleanOpenRuntimes();
+ fin = await conn();
+ });
describe('getInfo()', () => {
it('Fulfilled', () => fin.System.getAllExternalApplications().
diff --git a/test/external-services.test.ts b/test/external-services.test.ts
new file mode 100644
index 00000000..8fd0923b
--- /dev/null
+++ b/test/external-services.test.ts
@@ -0,0 +1,118 @@
+import * as assert from 'assert';
+import { conn } from './connect';
+import { Fin, launch } from '../src/main';
+import * as path from 'path';
+import { cleanOpenRuntimes } from './multi-runtime-utils';
+import { delayPromise } from './delay-promise';
+import * as sinon from 'sinon';
+import * as fs from 'fs';
+
+describe ('External Services', () => {
+ let fin: Fin;
+ let appConfig: any;
+
+ beforeEach(async () => {
+ await cleanOpenRuntimes();
+ appConfig = JSON.parse(fs.readFileSync(path.resolve('test/app.json')).toString());
+ fin = await conn();
+ });
+
+ after(async () => {
+ const apps = await fin.System.getAllApplications();
+ await Promise.all(apps.map(a => {
+ const { uuid } = a;
+ return fin.Application.wrap({uuid}).then(app => app.close());
+ }));
+ });
+
+ // tslint:disable-next-line
+ describe('External Provider', function () {
+
+ it('Should be able to register as Provider', function(done: any) {
+ // tslint:disable-next-line no-invalid-this
+ this.timeout(8000);
+
+ const url = appConfig.startup_app.url;
+ const newUrl = url.slice(0, url.lastIndexOf('/')) + '/client.html';
+
+ const clientConfig : any = {
+ ...appConfig,
+ 'startup_app': {
+ 'name': 'Services',
+ 'description': 'services test app',
+ 'url': newUrl,
+ 'uuid': 'service-client-test',
+ 'autoShow': true,
+ 'saveWindowState': false,
+ 'nonPersistent': true,
+ 'experimental': {
+ 'v2Api': true
+ }
+ }
+ };
+
+ fs.writeFileSync(path.resolve('test/client.json'), JSON.stringify(clientConfig));
+
+ async function test () {
+ const spy = sinon.spy();
+ const provider = await fin.Service.register();
+ provider.register('test', () => {
+ spy();
+ return 'return-test';
+ });
+ provider.onConnection(c => {
+ spy();
+ });
+ await launch({manifestUrl: path.resolve('test', 'client.json')});
+ await fin.InterApplicationBus.subscribe({uuid: 'service-client-test'}, 'return', (msg: any) => {
+ assert(spy.calledTwice && msg === 'return-test', 'Did not get IAB from dispatch');
+ done();
+ });
+ await delayPromise(1000);
+ await fin.InterApplicationBus.publish('start', 'hi');
+ }
+ test();
+ });
+
+ });
+
+ // tslint:disable-next-line
+ describe('External Client', function () {
+
+ it('Should be able to connect as Client', function(done: any) {
+ // tslint:disable-next-line no-invalid-this
+ this.timeout(8000);
+
+ const url = appConfig.startup_app.url;
+ const newUrl = url.slice(0, url.lastIndexOf('/')) + '/service.html';
+
+ const serviceConfig : any = {
+ ...appConfig,
+ 'startup_app': {
+ 'name': 'Service Provider',
+ 'description': 'Service Provider test app',
+ 'url': newUrl,
+ 'uuid': 'service-provider-test',
+ 'autoShow': true,
+ 'saveWindowState': false,
+ 'nonPersistent': true,
+ 'experimental': {
+ 'v2Api': true
+ }
+ }
+ };
+
+ fs.writeFileSync(path.resolve('test/service.json'), JSON.stringify(serviceConfig));
+
+ async function test() {
+ await launch({manifestUrl: path.resolve('test', 'service.json')});
+ const client = await fin.Service.connect({uuid: 'service-provider-test'});
+ client.dispatch('test').then(res => {
+ assert(res === 'return-test');
+ done();
+ });
+ }
+ test();
+ });
+ });
+});
diff --git a/test/frame.test.ts b/test/frame.test.ts
index d9e1ea36..87bb9e2c 100644
--- a/test/frame.test.ts
+++ b/test/frame.test.ts
@@ -1,13 +1,15 @@
import { conn } from './connect';
import * as assert from 'assert';
import { Fin, Frame } from '../src/main';
+import { cleanOpenRuntimes } from './multi-runtime-utils';
describe('Frame.', () => {
let fin: Fin;
let testFrame: Frame;
- before(() => {
- return conn().then(a => fin = a);
+ before(async () => {
+ await cleanOpenRuntimes();
+ fin = await conn();
});
beforeEach(() => {
diff --git a/test/interappbus.test.ts b/test/interappbus.test.ts
index b4367141..e6501f15 100644
--- a/test/interappbus.test.ts
+++ b/test/interappbus.test.ts
@@ -1,6 +1,7 @@
import { conn } from './connect';
import * as assert from 'assert';
import { connect as rawConnect, Fin } from '../src/main';
+import { cleanOpenRuntimes } from './multi-runtime-utils';
const id = 'adapter-test-window';
const topic = 'topic';
@@ -15,8 +16,12 @@ function noop() { }
describe('InterApplicationBus.', () => {
let fin: Fin;
- beforeEach(() => {
- return conn().then((a: Fin) => fin = a);
+ before(async () => {
+ await cleanOpenRuntimes();
+ });
+
+ beforeEach(async () => {
+ fin = await conn();
});
it('subscribe()', (done) => {
diff --git a/test/launcher.test.ts b/test/launcher.test.ts
index 475dd19f..7775e820 100644
--- a/test/launcher.test.ts
+++ b/test/launcher.test.ts
@@ -4,7 +4,8 @@ import * as os from 'os';
import * as path from 'path';
import Launcher from '../src/launcher/launcher';
import { download, getRuntimePath, OsConfig, getUrl } from '../src/launcher/nix-launch';
-import { resolveRuntimeVersion, rmDir, promiseMap } from '../src/launcher/util';
+import { resolveRuntimeVersion, rmDir } from '../src/launcher/util';
+import { promiseMap } from '../src/util/promises';
describe('Launcher', () => {
describe('Resolve Runtime', () => {
diff --git a/test/multi-runtime-application.test.ts b/test/multi-runtime-application.test.ts
index f9c72d21..97d69927 100644
--- a/test/multi-runtime-application.test.ts
+++ b/test/multi-runtime-application.test.ts
@@ -1,15 +1,19 @@
+/* tslint:disable:no-invalid-this no-function-expression insecure-random mocha-no-side-effect-code */
+import { conn } from './connect';
+import { Fin } from '../src/main';
import * as assert from 'assert';
import { delayPromise } from './delay-promise';
-import { cleanOpenRuntimes, DELAY_MS, TEST_TIMEOUT, launchX } from './multi-runtime-utils';
-import { clean } from './connect';
+import { cleanOpenRuntimes, DELAY_MS, TEST_TIMEOUT, launchAndConnect } from './multi-runtime-utils';
-describe('Multi Runtime', () => {
+describe('Multi Runtime', function () {
let appConfigTemplate: any;
- before(() => {
- clean();
- });
+ let fin: Fin;
+
+ describe('Application', function () {
- describe('Application', () => {
+ this.retries(2);
+ this.slow(TEST_TIMEOUT / 2 );
+ this.timeout(TEST_TIMEOUT);
function getAppConfig(): any {
const appConfigTemplate = {
@@ -22,27 +26,26 @@ describe('Multi Runtime', () => {
}
};
- // tslint:disable-next-line
appConfigTemplate.uuid += Math.floor(Math.random() * 10000);
return appConfigTemplate;
}
- beforeEach(() => {
- appConfigTemplate = getAppConfig();
+ before(async () => {
+ fin = await conn();
});
- afterEach(async () => {
+ beforeEach(async function () {
+ appConfigTemplate = getAppConfig();
return await cleanOpenRuntimes();
});
- describe('getInfo', () => {
+ describe('getInfo', function () {
+
it('should return the application Information', async function () {
- // tslint:disable-next-line no-invalid-this
- this.timeout(TEST_TIMEOUT);
const expectedLaunchMode = 'adapter';
- const conns = await launchX(2);
- const finA = conns[0];
- const finB = conns[1];
+ const [finA, finB] = await Promise.all([launchAndConnect(), launchAndConnect()]);
+ await delayPromise(DELAY_MS);
+
const realApp = await finB.Application.create(appConfigTemplate);
await realApp.run();
const app = await finA.Application.wrap({ uuid: appConfigTemplate.uuid });
@@ -52,13 +55,10 @@ describe('Multi Runtime', () => {
});
});
- describe('getParentUuid', () => {
+ describe('getParentUuid', function () {
+
it('should return the uuid of the parent adapter connection', async function () {
- // tslint:disable-next-line no-invalid-this
- this.timeout(TEST_TIMEOUT);
- const conns = await launchX(2);
- const finA = conns[0];
- const finB = conns[1];
+ const [finA, finB] = await Promise.all([launchAndConnect(), launchAndConnect()]);
const expectedUuid = finB.wire.me.uuid;
await delayPromise(DELAY_MS);
@@ -72,13 +72,10 @@ describe('Multi Runtime', () => {
});
});
- describe('isRunning', () => {
+ describe('isRunning', function () {
+
it('should return the running state of an application', async function () {
- // tslint:disable-next-line no-invalid-this
- this.timeout(TEST_TIMEOUT);
- const conns = await launchX(2);
- const finA = conns[0];
- const finB = conns[1];
+ const [finA, finB] = await Promise.all([launchAndConnect(), launchAndConnect()]);
await delayPromise(DELAY_MS);
const realApp = await finB.Application.create(appConfigTemplate);
diff --git a/test/multi-runtime-events.test.ts b/test/multi-runtime-events.test.ts
index 88437041..11ee2626 100644
--- a/test/multi-runtime-events.test.ts
+++ b/test/multi-runtime-events.test.ts
@@ -1,8 +1,15 @@
+/* tslint:disable:no-invalid-this no-function-expression insecure-random mocha-no-side-effect-code no-empty */
+import { conn } from './connect';
+import { Fin } from '../src/main';
import * as assert from 'assert';
import { delayPromise } from './delay-promise';
-import { launchAndConnect, cleanOpenRuntimes, DELAY_MS, TEST_TIMEOUT, launchX } from './multi-runtime-utils';
+import { launchAndConnect, cleanOpenRuntimes, DELAY_MS, TEST_TIMEOUT } from './multi-runtime-utils';
-describe('Multi Runtime', () => {
+describe('Multi Runtime', function() {
+ let fin: Fin;
+
+ this.slow(TEST_TIMEOUT / 2 );
+ this.timeout(TEST_TIMEOUT * 2);
function getAppConfig() {
const appConfigTemplate = {
@@ -13,58 +20,55 @@ describe('Multi Runtime', () => {
saveWindowState: false,
accelerator: {
devtools: true
+ },
+ experimental: {
+ v2api: true
}
};
- // tslint:disable-next-line
appConfigTemplate.uuid += Math.floor(Math.random() * 1000);
return appConfigTemplate;
}
- afterEach(async () => {
+ before(async () => {
+ fin = await conn();
+ });
+
+ beforeEach(async function() {
return await cleanOpenRuntimes();
});
- describe('Events', () => {
+ describe('Events', function() {
- describe('Launch then subscribe', () => {
- describe('System', () => {
- // tslint:disable-next-line
- it('should raise application closed events', function (done: Function) {
- // tslint:disable-next-line no-invalid-this
- this.timeout(TEST_TIMEOUT * 2);
+ describe('Launch then subscribe', function() {
+ describe('System', function() {
+ it('should raise application started events', function (done: Function) {
async function test() {
const appConfig = getAppConfig();
- const conns = await launchX(2);
- const finA = conns[0];
- const finB = conns[1];
+ const [finA, finB] = await Promise.all([launchAndConnect(), launchAndConnect()]);
await delayPromise(DELAY_MS);
-
const realApp = await finB.Application.create(appConfig);
- await realApp.run();
+ finA.System.on('application-started', async (e: any) => {
+ assert.equal(e.type, 'application-started', 'Expected event type to match event');
+ await realApp.close();
- finA.System.on('application-closed', (e: any) => {
- assert.equal(e.type, 'application-closed', 'Expected event type to match event');
done();
});
-
await delayPromise(DELAY_MS);
- await realApp.close();
- await delayPromise(1500);
+
+ return await realApp.run();
}
- test();
+ test().catch(() => cleanOpenRuntimes());
});
it('should raise application created events', function (done: Function) {
- // tslint:disable-next-line no-invalid-this
- this.timeout(TEST_TIMEOUT * 2);
async function test() {
- const conns = await launchX(2);
- const finA = conns[0];
- const finB = conns[1];
+ const [finA, finB] = await Promise.all([launchAndConnect(), launchAndConnect()]);
+ await delayPromise(DELAY_MS);
+
const appConfig = getAppConfig();
await delayPromise(DELAY_MS);
let realApp: any;
@@ -82,51 +86,42 @@ describe('Multi Runtime', () => {
await delayPromise(DELAY_MS);
}
- test();
+ test().catch(() => cleanOpenRuntimes());
});
});
});
- describe('Launch then subscribe', () => {
- describe('application', () => {
- // tslint:disable-next-line
- it.skip('should raise closed events', function (done: Function) {
- // tslint:disable-next-line no-invalid-this
- this.timeout(TEST_TIMEOUT);
-
+ describe('Launch then subscribe', function() {
+ describe('Application', function() {
+ it('should raise application started events', function (done: Function) {
async function test() {
const appConfig = getAppConfig();
- const conns = await launchX(2);
- const finA = conns[0];
- const finB = conns[1];
+ const [finA, finB] = await Promise.all([launchAndConnect(), launchAndConnect()]);
await delayPromise(DELAY_MS);
-
- const realApp = await finB.Application.create(appConfig.uuid);
- await realApp.run();
+ const realApp = await finB.Application.create(appConfig);
const app = await finA.Application.wrap({ uuid: appConfig.uuid });
+ await delayPromise(DELAY_MS);
+
+ app.on('started', async (e: any) => {
+ assert.equal(e.type, 'started', 'Expected event type to match event');
+ await app.close();
- app.on('closed', (e: any) => {
- assert.equal(e.type, 'closed', 'Expected event type to match event');
done();
});
await delayPromise(DELAY_MS);
- await realApp.close();
- await delayPromise(DELAY_MS);
+ await realApp.run();
}
- test();
+ test().catch((err) => {
+ cleanOpenRuntimes();
+ });
});
it('should raise initialized events', function (done: () => void) {
- // tslint:disable-next-line no-invalid-this
- this.timeout(TEST_TIMEOUT);
-
async function test() {
- const conns = await launchX(2);
- const finA = conns[0];
- const finB = conns[1];
const appConfig = getAppConfig();
+ const [finA, finB] = await Promise.all([launchAndConnect(), launchAndConnect()]);
await delayPromise(DELAY_MS);
const realApp = await finB.Application.create(appConfig);
@@ -143,24 +138,20 @@ describe('Multi Runtime', () => {
await delayPromise(DELAY_MS);
}
- test();
+ test().catch(() => cleanOpenRuntimes());
});
});
});
- describe('Launch then subscribe', () => {
- describe('Window', () => {
+ describe('Launch then subscribe', function() {
+ describe('Window', function() {
it('should raise initialized', function (done: (value: void) => void) {
- // tslint:disable-next-line no-invalid-this
- this.timeout(TEST_TIMEOUT);
-
async function test() {
const appConfig = getAppConfig();
- const conns = await launchX(2);
- const finA = conns[0];
- const finB = conns[1];
+ const [finA, finB] = await Promise.all([launchAndConnect(), launchAndConnect()]);
await delayPromise(DELAY_MS);
+
const app = await finA.Application.wrap({ uuid: appConfig.uuid });
const win = await app.getWindow();
@@ -175,26 +166,24 @@ describe('Multi Runtime', () => {
await delayPromise(DELAY_MS);
}
- test();
+ test().catch(() => cleanOpenRuntimes());
});
});
});
- describe('Subscribe then launch', () => {
+ describe('Subscribe then launch', function() {
- describe('System', () => {
+ describe('System', function() {
- // tslint:disable-next-line
- it('should raise application closed events', function (done: Function) {
- // tslint:disable-next-line no-invalid-this
+ it('should raise application started events', function (done: Function) {
this.timeout(TEST_TIMEOUT * 2);
async function test() {
const appConfig = getAppConfig();
const finA = await launchAndConnect();
await delayPromise(DELAY_MS);
- finA.System.on('application-closed', (e: any) => {
- assert.equal(e.type, 'application-closed', 'Expected event type to match event');
+ finA.System.on('application-started', (e: any) => {
+ assert.equal(e.type, 'application-started', 'Expected event type to match event');
done();
});
const finB = await launchAndConnect();
@@ -207,22 +196,15 @@ describe('Multi Runtime', () => {
await delayPromise(1500);
}
- test();
+ test().catch(() => cleanOpenRuntimes());
});
- it('should raise application-started events', function (done: (value: void) => void) {
- // tslint:disable-next-line no-invalid-this
+ it('should raise application-created events', function (done: (value: void) => void) {
this.timeout(TEST_TIMEOUT * 2); //We need a bit more time for these tests.
async function test() {
const appConfig = getAppConfig();
- const argsConnect = [
- '--security-realm=supersecret',
- '--enable-mesh',
- '--enable-multi-runtime',
- '--v=1'
- ];
- const finA = await launchAndConnect(undefined, undefined, 'supersecret', argsConnect);
+ const finA = await launchAndConnect();
await delayPromise(DELAY_MS);
const app = await finA.Application.wrap({ uuid: appConfig.uuid });
@@ -242,20 +224,17 @@ describe('Multi Runtime', () => {
await delayPromise(DELAY_MS);
}
- test();
+ test().catch(() => cleanOpenRuntimes());
});
});
});
- describe('Subscribe then launch', () => {
+ describe('Subscribe then launch', function() {
- describe('Application', () => {
+ describe('Application', function() {
- //Bug regarding Application/Window close events.
- // tslint:disable-next-line
- it.skip('should raise closed events', function (done: Function) {
- // tslint:disable-next-line no-invalid-this
+ it('should raise started events', function (done: Function) {
this.timeout(TEST_TIMEOUT * 2);
async function test() {
@@ -263,8 +242,9 @@ describe('Multi Runtime', () => {
const finA = await launchAndConnect();
await delayPromise(DELAY_MS);
const app = await finA.Application.wrap({ uuid: appConfig.uuid });
- app.on('closed', (e: any) => {
- assert.equal(e.type, 'closed', 'Expected event type to match event');
+ app.on('started', async (e: any) => {
+ assert.equal(e.type, 'started', 'Expected event type to match event');
+ await app.close();
done();
});
const finB = await launchAndConnect();
@@ -273,25 +253,17 @@ describe('Multi Runtime', () => {
await realApp.run();
await delayPromise(DELAY_MS);
- await realApp.close();
- await delayPromise(DELAY_MS);
}
- test();
+ test().catch(() => cleanOpenRuntimes());
});
- it('should raise initialized events', function (done: (value: void) => void) {
- // tslint:disable-next-line no-invalid-this
+ it('should raise initialized eventsss', function (done: (value: void) => void) {
this.timeout(TEST_TIMEOUT * 2); //We need a bit more time for these tests.
async function test() {
const appConfig = getAppConfig();
- const argsConnect = [
- '--enable-mesh',
- '--enable-multi-runtime',
- '--v=1'
- ];
- const finA = await launchAndConnect(undefined, undefined, 'supersecret', argsConnect);
+ const finA = await launchAndConnect();
await delayPromise(DELAY_MS);
const app = await finA.Application.wrap({ uuid: appConfig.uuid });
@@ -311,17 +283,16 @@ describe('Multi Runtime', () => {
await delayPromise(DELAY_MS);
}
- test();
+ test().catch(() => cleanOpenRuntimes());
});
});
});
- describe('Subscribe then launch', () => {
- describe('Window', () => {
+ describe('Subscribe then launch', function() {
+ describe('Window', function() {
it('should raise bounds-changed', function (done: (value: void) => void) {
- // tslint:disable-next-line no-invalid-this
this.timeout(TEST_TIMEOUT * 2); //We need a bit more time for these tests.
async function test() {
@@ -346,11 +317,10 @@ describe('Multi Runtime', () => {
await delayPromise(DELAY_MS);
}
- test();
+ test().catch(() => cleanOpenRuntimes());
});
it('should raise hidden', function (done: (value: void) => void) {
- // tslint:disable-next-line no-invalid-this
this.timeout(TEST_TIMEOUT * 2); //We need a bit more time for these tests.
async function test() {
@@ -359,6 +329,8 @@ describe('Multi Runtime', () => {
const app = await finA.Application.wrap({ uuid: appConfig.uuid });
const win = await app.getWindow();
+ await delayPromise(DELAY_MS);
+
win.on('hidden', (e: any) => {
assert.equal(e.type, 'hidden', 'Expected event type to match event');
win.close().then(done);
@@ -373,7 +345,7 @@ describe('Multi Runtime', () => {
await delayPromise(DELAY_MS);
}
- test();
+ test().catch(() => cleanOpenRuntimes());
});
});
diff --git a/test/multi-runtime-interappbus.test.ts b/test/multi-runtime-interappbus.test.ts
index 4a44e243..ff6b55fb 100644
--- a/test/multi-runtime-interappbus.test.ts
+++ b/test/multi-runtime-interappbus.test.ts
@@ -1,22 +1,30 @@
+/* tslint:disable:no-invalid-this no-function-expression insecure-random mocha-no-side-effect-code no-empty */
+import { conn } from './connect';
+import { Fin } from '../src/main';
import * as assert from 'assert';
import { delayPromise } from './delay-promise';
-import { cleanOpenRuntimes, DELAY_MS, TEST_TIMEOUT, launchX } from './multi-runtime-utils';
+import { cleanOpenRuntimes, DELAY_MS, TEST_TIMEOUT, launchAndConnect } from './multi-runtime-utils';
describe('Multi Runtime', function () {
+ let fin: Fin;
- afterEach(async () => {
+ this.retries(2);
+ this.slow(TEST_TIMEOUT / 2 );
+ this.timeout(TEST_TIMEOUT);
+
+ before(async () => {
+ fin = await conn();
+ });
+
+ beforeEach(async function () {
return await cleanOpenRuntimes();
});
- describe('InterApplicationBus', () => {
- it('should subscribe to * and publish', function (done: Function) {
- // tslint:disable-next-line no-invalid-this
- this.timeout(TEST_TIMEOUT);
+ describe('InterApplicationBus', function () {
+ it('should subscribe to * and publish', function (done: any) {
async function test() {
- const conns = await launchX(2);
- const finA = conns[0];
- const finB = conns[1];
+ const [finA, finB] = await Promise.all([launchAndConnect(), launchAndConnect()]);
const topic = 'my-topic';
const data = 'hello';
@@ -27,22 +35,24 @@ describe('Multi Runtime', function () {
assert.equal(data, message, 'Expected message to be the data sent');
done();
});
+ await delayPromise(DELAY_MS);
return await finB.InterApplicationBus.publish('my-topic', data);
}
- test();
+ test().catch(err => {
+ cleanOpenRuntimes().then(done(err));
+ });
});
- it('should subscribe to a uuid and publish', function (done: Function) {
- // tslint:disable-next-line no-invalid-this
- this.timeout(TEST_TIMEOUT);
+ it('should subscribe to a uuid and publish', function (done: any) {
async function test() {
- const conns = await launchX(2);
- const finA = conns[0];
- const finB = conns[1];
+ const [finA, finB] = await Promise.all([launchAndConnect(), launchAndConnect()]);
const topic = 'my-topic';
const data = 'hello';
+
+ await delayPromise(DELAY_MS);
+
await finA.InterApplicationBus
.subscribe({ uuid: finB.wire.me.uuid }, topic, (message: any, source: any) => {
assert.equal(finB.wire.me.uuid, source.uuid, 'Expected source to be runtimeB');
@@ -53,20 +63,20 @@ describe('Multi Runtime', function () {
await finB.InterApplicationBus.publish(topic, data);
}
- test();
+ test().catch(err => {
+ cleanOpenRuntimes().then(done(err));
+ });
});
- it('should subscribe to * and send', function (done: Function) {
- // tslint:disable-next-line no-invalid-this
- this.timeout(TEST_TIMEOUT);
+ it('should subscribe to * and send', function (done: any) {
async function test() {
- const conns = await launchX(2);
- const finA = conns[0];
- const finB = conns[1];
+ const [finA, finB] = await Promise.all([launchAndConnect(), launchAndConnect()]);
const topic = 'my-topic';
const data = 'hello';
+ await delayPromise(DELAY_MS);
+
await finA.InterApplicationBus.subscribe({ uuid: '*' }, topic, (message: any, source: any) => {
assert.equal(finB.wire.me.uuid, source.uuid, 'Expected source to be runtimeB');
assert.equal(data, message, 'Expected message to be the data sent');
@@ -77,20 +87,20 @@ describe('Multi Runtime', function () {
}
- test();
+ test().catch(err => {
+ cleanOpenRuntimes().then(done(err));
+ });
});
- it('should subscribe to uuid and send', function (done: Function) {
- // tslint:disable-next-line no-invalid-this
- this.timeout(TEST_TIMEOUT);
+ it('should subscribe to uuid and send', function (done: any) {
async function test() {
- const conns = await launchX(2);
- const finA = conns[0];
- const finB = conns[1];
+ const [finA, finB] = await Promise.all([launchAndConnect(), launchAndConnect()]);
const topic = 'my-topic';
const data = 'hello';
+ await delayPromise(DELAY_MS);
+
await finA.InterApplicationBus.subscribe({ uuid: finB.wire.me.uuid },
topic, (message: any, source: any) => {
assert.equal(finB.wire.me.uuid, source.uuid, 'Expected source to be runtimeB');
@@ -103,58 +113,58 @@ describe('Multi Runtime', function () {
await finB.InterApplicationBus.send({ uuid: finA.wire.me.uuid }, topic, data);
}
- test();
+ test().catch(err => {
+ cleanOpenRuntimes().then(done(err));
+ });
});
- it('should get subscriberAdded Events', function (done: Function) {
- // tslint:disable-next-line no-invalid-this
- this.timeout(TEST_TIMEOUT);
+ it('should get subscriberAdded Events', function (done: any) {
async function test() {
- const conns = await launchX(2);
- const finA = conns[0];
- const finB = conns[1];
+ const [finA, finB] = await Promise.all([launchAndConnect(), launchAndConnect()]);
const topic = 'my-topic';
const expectedUuid = finB.wire.me.uuid;
+ await delayPromise(DELAY_MS);
+
await finA.InterApplicationBus.on('subscriber-added', (sub: any, b: any) => {
assert.equal(expectedUuid, sub.uuid, 'Expected UUIDs to match');
assert.equal(sub.topic, topic, 'Expected topics to match');
done();
});
await delayPromise(DELAY_MS);
- // tslint:disable-next-line
- return await finB.InterApplicationBus.subscribe({ uuid: finA.wire.me.uuid }, 'my-topic', () => { });
+ return await finB.InterApplicationBus.subscribe({ uuid: finA.wire.me.uuid }, 'my-topic', function () { });
}
- test();
+ test().catch(err => {
+ cleanOpenRuntimes().then(done(err));
+ });
});
- it('should get subscriberRemoved Events', function (done: Function) {
- // tslint:disable-next-line no-invalid-this
- this.timeout(TEST_TIMEOUT);
+ it('should get subscriberRemoved Events', function (done: any) {
async function test() {
- const conns = await launchX(2);
- const finA = conns[0];
- const finB = conns[1];
+ const [finA, finB] = await Promise.all([launchAndConnect(), launchAndConnect()]);
const topic = 'my-topic';
const expectedUuid = finB.wire.me.uuid;
+ await delayPromise(DELAY_MS);
+
await finA.InterApplicationBus.on('subscriber-removed', (sub: any, b: any) => {
assert.equal(expectedUuid, sub.uuid, 'Expected UUIDs to match');
assert.equal(sub.topic, topic, 'Expected topics to match');
done();
});
- // tslint:disable-next-line
- function listener() { };
+ function listener() { }
await finB.InterApplicationBus.subscribe({ uuid: finA.wire.me.uuid }, topic, listener);
await delayPromise(DELAY_MS);
await finB.InterApplicationBus.unsubscribe({ uuid: finA.wire.me.uuid }, topic, listener);
}
- test();
+ test().catch(err => {
+ cleanOpenRuntimes().then(done(err));
+ });
});
});
diff --git a/test/multi-runtime-system.test.ts b/test/multi-runtime-system.test.ts
index 78d636a3..3dec1ed3 100644
--- a/test/multi-runtime-system.test.ts
+++ b/test/multi-runtime-system.test.ts
@@ -1,8 +1,16 @@
+/* tslint:disable:no-invalid-this no-function-expression insecure-random mocha-no-side-effect-code no-empty */
+import { conn } from './connect';
+import { Fin } from '../src/main';
import * as assert from 'assert';
import { delayPromise } from './delay-promise';
-import { launchX, cleanOpenRuntimes, DELAY_MS, TEST_TIMEOUT, getRuntimeProcessInfo } from './multi-runtime-utils';
+import { launchAndConnect, cleanOpenRuntimes, DELAY_MS, TEST_TIMEOUT, getRuntimeProcessInfo } from './multi-runtime-utils';
-describe('Multi Runtime', () => {
+describe('Multi Runtime', function () {
+ let fin: Fin;
+
+ this.retries(2);
+ this.slow(TEST_TIMEOUT / 2 );
+ this.timeout(TEST_TIMEOUT);
function getAppConfig() {
const appConfigTemplate = {
@@ -16,31 +24,31 @@ describe('Multi Runtime', () => {
}
};
- // tslint:disable-next-line
appConfigTemplate.uuid += Math.floor(Math.random() * 10000);
return appConfigTemplate;
}
- afterEach(async () => {
+ before(async () => {
+ fin = await conn();
+ });
+
+ beforeEach(async function () {
return await cleanOpenRuntimes();
});
- describe('System', () => {
+ describe('System', function () {
- describe('getAllApplications', () => {
+ describe('getAllApplications', function () {
it('should return the application information from all runtimes', async function() {
- // tslint:disable-next-line no-invalid-this
this.timeout(TEST_TIMEOUT);
const appConfigA = getAppConfig();
const appConfigB = getAppConfig();
const appConfigC = getAppConfig();
const appConfigD = getAppConfig();
- const conns = await launchX(3);
- // Why delay here?
- await delayPromise(DELAY_MS);
- const [finA, finB, finC] = conns;
+ const [finA, finB, finC] = await Promise.all([launchAndConnect(), launchAndConnect(), launchAndConnect()]);
+ await delayPromise(DELAY_MS);
const [appA, appB, appC, appD] = await Promise.all([finA.Application.create(appConfigA),
finB.Application.create(appConfigB),
@@ -61,15 +69,14 @@ describe('Multi Runtime', () => {
});
});
- describe('getAllExternalApplications', () => {
+ describe('getAllExternalApplications', function () {
it('should return the external application information from all runtimes', async function() {
- // tslint:disable-next-line no-invalid-this
this.timeout(TEST_TIMEOUT);
- const conns = await launchX(3);
+ const conns = await Promise.all([launchAndConnect(), launchAndConnect(), launchAndConnect()]);
+ const finA = conns[0];
await delayPromise(DELAY_MS);
- const [finA] = conns;
const connStrings = conns.map(f => {
const conn = getRuntimeProcessInfo(f);
return `${conn.version}/${conn.port}/${conn.realm}`;
@@ -87,21 +94,17 @@ describe('Multi Runtime', () => {
});
});
- describe('getAllWindows', () => {
+ describe('getAllWindows', function () {
it('should return the window information from all runtimes', async function() {
- // tslint:disable-next-line no-invalid-this
this.timeout(TEST_TIMEOUT);
const appConfigA = getAppConfig();
const appConfigB = getAppConfig();
const appConfigC = getAppConfig();
const appConfigD = getAppConfig();
- const conns = await launchX(3);
- // Why delay here?
+ const [finA, finB, finC] = await Promise.all([launchAndConnect(), launchAndConnect(), launchAndConnect()]);
await delayPromise(DELAY_MS);
- const [finA, finB, finC] = conns;
-
const [appA, appB, appC, appD] = await Promise.all([finA.Application.create(appConfigA),
finB.Application.create(appConfigB),
finB.Application.create(appConfigC),
@@ -128,21 +131,17 @@ describe('Multi Runtime', () => {
});
});
- describe('getProcessList', () => {
+ describe('getProcessList', function () {
it('should return the process information from all runtimes', async function() {
- // tslint:disable-next-line no-invalid-this
this.timeout(TEST_TIMEOUT);
const appConfigA = getAppConfig();
const appConfigB = getAppConfig();
const appConfigC = getAppConfig();
const appConfigD = getAppConfig();
- const conns = await launchX(3);
- // Why delay here?
+ const [finA, finB, finC] = await Promise.all([launchAndConnect(), launchAndConnect(), launchAndConnect()]);
await delayPromise(DELAY_MS);
- const [finA, finB, finC] = conns;
-
const [appA, appB, appC, appD] = await Promise.all([finA.Application.create(appConfigA),
finB.Application.create(appConfigB),
finB.Application.create(appConfigC),
diff --git a/test/multi-runtime-utils.ts b/test/multi-runtime-utils.ts
index 166a1232..6d6a8505 100644
--- a/test/multi-runtime-utils.ts
+++ b/test/multi-runtime-utils.ts
@@ -1,3 +1,4 @@
+/* tslint:disable:no-invalid-this no-function-expression insecure-random mocha-no-side-effect-code no-empty */
import * as https from 'https';
import * as os from 'os';
import * as rimraf from 'rimraf';
@@ -5,7 +6,8 @@ import * as fs from 'fs';
import * as path from 'path';
import * as ChildProcess from 'child_process';
import { connect as rawConnect, Fin } from '../src/main';
-import { resolveDir, first, serial, promiseMap } from '../src/launcher/util';
+import { resolveDir, first } from '../src/launcher/util';
+import { serial, promiseMap } from '../src/util/promises';
import { delayPromise } from './delay-promise';
const appConfig = JSON.parse(fs.readFileSync(path.resolve('test/app.json')).toString());
@@ -14,7 +16,8 @@ let uuidNum = 0;
let runtimes: Array = [];
-export const DELAY_MS = 100;
+let ws_port = 8690;
+export const DELAY_MS = 1000;
export const TEST_TIMEOUT = 30 * 1000;
export interface RuntimeProcess {
@@ -23,22 +26,23 @@ export interface RuntimeProcess {
port: string;
version: string;
fin?: Fin;
+ runtime: any;
}
async function spawnRealm(version: string, realm?: string, args?: Array): Promise {
- // tslint:disable-next-line
return new Promise((resolve, reject) => {
- // tslint:disable-next-line no-function-expression
resolveOpenFinVersion(version).then(async function(returnedVersion: string) {
try {
- // tslint:disable-next-line
- const realm = `test_realm_${ Math.random() }`;
- //const cacheDir = await realmCachePath(realm);
+ const realmArg = args && args.find(str => str.indexOf('security-realm') > -1);
+ const realmValue = realmArg && realmArg.split('=')[1];
+ const realm = realmValue ? realmValue : `test_realm_${ Math.random() }`;
const ofCacheFolder = path.resolve(process.env.LOCALAPPDATA, 'OpenFin', 'cache');
const cacheDir = path.resolve(ofCacheFolder, realm);
const appConfig = generateAppConfig();
- const configLocation = path.resolve(cacheDir, `${appConfig.startup_app.uuid}.json`);
+ const configLocation = path.resolve(cacheDir, `${appConfig._startup_app.uuid}.json`);
+ // tslint:disable-next-line
+ const port = ++ws_port;
args = args || [
'--enable-multi-runtime',
@@ -51,36 +55,28 @@ async function spawnRealm(version: string, realm?: string, args?: Array)
args.push(`--startup-url=${configLocation}`);
fs.mkdirSync(cacheDir);
-
+ appConfig.websocket_port = port;
fs.writeFileSync(configLocation, JSON.stringify(appConfig));
const ofEXElocation = versionPath(returnedVersion);
+ const opts = {
+ env: {
+ ELECTRON_NO_ATTACH_CONSOLE: 1
+ },
+ detached: true
- const runtime = ChildProcess.spawn(ofEXElocation, args);
-
- runtime.on('error', reject);
-
- // tslint:disable-next-line no-function-expression
- const portSniffer = function(data: any) {
- const sData = '' + data;
- const matched = /^Opened on (\d+)/.exec(sData);
-
- if (matched && matched.length > 1 ) {
- const port = matched[1];
-
- runtime.stdout.removeListener('data', portSniffer);
-
- resolve({
- appConfig,
- port,
- runtime,
- realm,
- version: returnedVersion
- });
- }
};
+ const runtime = ChildProcess.spawn(ofEXElocation, args, opts);
- runtime.stdout.on('data', portSniffer);
+ await delayPromise(DELAY_MS);
+
+ resolve({
+ appConfig,
+ port,
+ runtime,
+ realm,
+ version: returnedVersion
+ });
} catch (e) {
reject(e);
@@ -119,18 +115,18 @@ function generateAppConfig(): any {
return {
uuid,
- // tslint:disable-next-line
- startup_app: {
+ _startup_app: {
uuid,
name: uuid,
autoShow: true,
url: appConfig.startup_app.url,
- saveWindowState: false
+ saveWindowState: false,
+ experimental: appConfig.startup_app.experimental,
+ nonPersistent: true
}
};
}
function resolveOpenFinVersion(version: string): Promise {
- // tslint:disable-next-line
return new Promise ((resolve, reject) => {
// match point version eg. 6.29.17.14, fail on channels
@@ -182,7 +178,6 @@ do taskkill /f /pid %a`;
const cmd = `lsof -n -i4TCP:${port} | grep LISTEN | awk '{ print $2 }' | xargs kill`;
ChildProcess.execSync(cmd);
}
- // tslint:disable-next-line:no-empty
} catch (e) {
}
}
@@ -191,8 +186,12 @@ export function kill(fin: Fin) {
killByPort(getPort(fin));
}
+export function killByruntime(runtimeProcess: RuntimeProcess) {
+ runtimeProcess.runtime.kill();
+}
+
async function closeAndClean(runtimeProcess: RuntimeProcess): Promise {
- killByPort(runtimeProcess.port);
+ killByruntime(runtimeProcess);
// give some time for rvm process to be killed
await delayPromise(DELAY_MS);
const cachePath = await realmCachePath(runtimeProcess.realm);
@@ -200,7 +199,6 @@ async function closeAndClean(runtimeProcess: RuntimeProcess): Promise {
}
export async function launchAndConnect(version: string = process.env.OF_VER,
- // tslint:disable-next-line
uuid: string = `my-uuid ${appConfig.startup_app.uuid} ${Math.floor(Math.random() * 1000)}`,
realm?: string, args?: Array): Promise {
diff --git a/test/multi-runtime-window.test.ts b/test/multi-runtime-window.test.ts
index 243b06e1..5415a72a 100644
--- a/test/multi-runtime-window.test.ts
+++ b/test/multi-runtime-window.test.ts
@@ -1,8 +1,17 @@
+/* tslint:disable:no-invalid-this no-function-expression insecure-random mocha-no-side-effect-code no-empty */
+import { conn } from './connect';
+import { Fin } from '../src/main';
import * as assert from 'assert';
import { delayPromise } from './delay-promise';
-import { launchX, cleanOpenRuntimes, DELAY_MS, TEST_TIMEOUT } from './multi-runtime-utils';
+import { launchAndConnect, cleanOpenRuntimes, DELAY_MS, TEST_TIMEOUT } from './multi-runtime-utils';
+
+describe('Multi Runtime', function () {
+ let fin: Fin;
+
+ this.retries(2);
+ this.slow(TEST_TIMEOUT / 2 );
+ this.timeout(TEST_TIMEOUT);
-describe('Multi Runtime', () => {
let appConfigTemplate: any;
function getAppConfig() {
const appConfigTemplate = {
@@ -16,28 +25,26 @@ describe('Multi Runtime', () => {
}
};
- // tslint:disable-next-line
appConfigTemplate.uuid += Math.floor(Math.random() * 10000);
return appConfigTemplate;
}
- beforeEach(() => {
- appConfigTemplate = getAppConfig();
+ before(async () => {
+ fin = await conn();
});
- afterEach(async () => {
+
+ beforeEach(async function () {
+ appConfigTemplate = getAppConfig();
return await cleanOpenRuntimes();
});
- describe('Window', () => {
+ describe('Window', function () {
- describe('moveBy', () => {
+ describe('moveBy', function () {
it('should move the Window by the given values', async function() {
- // tslint:disable-next-line no-invalid-this
this.timeout(TEST_TIMEOUT);
- const conns = await launchX(2);
- const finA = conns[0];
- const finB = conns[1];
+ const [finA, finB] = await Promise.all([launchAndConnect(), launchAndConnect()]);
await delayPromise(DELAY_MS);
const realApp = await finB.Application.create(appConfigTemplate);
await realApp.run();
@@ -53,20 +60,16 @@ describe('Multi Runtime', () => {
});
});
- describe('resizeTo', () => {
+ describe('resizeTo', function () {
it('should resize the Window by the given values', async function() {
- // tslint:disable-next-line no-invalid-this
this.timeout(TEST_TIMEOUT);
const resizeToVal = 200;
- const conns = await launchX(2);
- const finA = conns[0];
- const finB = conns[1];
+ const [finA, finB] = await Promise.all([launchAndConnect(), launchAndConnect()]);
await delayPromise(DELAY_MS);
const realApp = await finB.Application.create(appConfigTemplate);
await realApp.run();
- const app = await finA.Application.wrap({ uuid: appConfigTemplate.uuid });
- const win = await app.getWindow();
+ const win = await finA.Window.wrap({ uuid: appConfigTemplate.uuid, name: appConfigTemplate.uuid});
const bounds = await win.getBounds();
await win.resizeTo(resizeToVal, resizeToVal, 'top-left');
const postResizeBounds = await win.getBounds();
@@ -82,19 +85,15 @@ describe('Multi Runtime', () => {
});
});
- describe('getState', () => {
+ describe('getState', function () {
it('should return the state of the Window', async function() {
- // tslint:disable-next-line no-invalid-this
this.timeout(TEST_TIMEOUT);
- const conns = await launchX(2);
- const finA = conns[0];
- const finB = conns[1];
+ const [finA, finB] = await Promise.all([launchAndConnect(), launchAndConnect()]);
await delayPromise(DELAY_MS);
const realApp = await finB.Application.create(appConfigTemplate);
await realApp.run();
- const app = await finA.Application.wrap({ uuid: appConfigTemplate.uuid });
- const win = await app.getWindow();
+ const win = await finA.Window.wrap({ uuid: appConfigTemplate.uuid, name: appConfigTemplate.uuid });
const state = await win.getState();
const expectedState = 'normal';
@@ -105,12 +104,9 @@ describe('Multi Runtime', () => {
});
it('should return the state of the Window post a minimize action', async function() {
- // tslint:disable-next-line no-invalid-this
this.timeout(TEST_TIMEOUT);
- const conns = await launchX(2);
- const finA = conns[0];
- const finB = conns[1];
+ const [finA, finB] = await Promise.all([launchAndConnect(), launchAndConnect()]);
await delayPromise(DELAY_MS);
const realApp = await finB.Application.create(appConfigTemplate);
await realApp.run();
diff --git a/test/multi-runtime.test.ts b/test/multi-runtime.test.ts
index b0b34f3d..d4bb998c 100644
--- a/test/multi-runtime.test.ts
+++ b/test/multi-runtime.test.ts
@@ -1,11 +1,22 @@
+/* tslint:disable:no-invalid-this no-function-expression insecure-random mocha-no-side-effect-code no-empty */
+import { conn } from './connect';
+import { Fin } from '../src/main';
import * as assert from 'assert';
import { delayPromise } from './delay-promise';
import { cleanOpenRuntimes, DELAY_MS, getRuntimeProcessInfo, launchAndConnect, TEST_TIMEOUT } from './multi-runtime-utils';
-import { serial } from '../src/launcher/util';
-describe('Multi Runtime', () => {
+describe('Multi Runtime', function() {
+ let fin: Fin;
- afterEach(async () => {
+ this.retries(2);
+ this.slow(TEST_TIMEOUT);
+ this.timeout(TEST_TIMEOUT);
+
+ before(async () => {
+ fin = await conn();
+ });
+
+ beforeEach(async function() {
return await cleanOpenRuntimes();
});
@@ -14,20 +25,17 @@ describe('Multi Runtime', () => {
return `${version}/${port}/${realm ? realm : ''}`;
}
- describe('Connections', () => {
+ describe('Connections', function() {
+
it('should respect the enable-mesh flag for security realms', async function() {
const argsConnect = [
- '--security-realm=superSecret'
+ `--security-realm=super-secret-${Math.floor(Math.random() * 1000)}`
];
- // tslint:disable-next-line no-invalid-this
- this.timeout(TEST_TIMEOUT);
- const conns = await serial([() => launchAndConnect(),
- () => launchAndConnect(undefined, undefined, undefined, argsConnect),
- () => launchAndConnect()]);
- const finA = conns[0];
- const finB = conns[1];
- const finC = conns[2];
+ const [ finA, finB, finC ] = await Promise.all([launchAndConnect(),
+ launchAndConnect(undefined, undefined, undefined, argsConnect),
+ launchAndConnect()]);
+
await delayPromise(DELAY_MS);
const apps = await finA.System.getAllExternalApplications();
const uuidList = apps.map((a: any) => { return a.uuid; });
diff --git a/test/notification.test.ts b/test/notification.test.ts
index 7f871263..991e384e 100644
--- a/test/notification.test.ts
+++ b/test/notification.test.ts
@@ -1,6 +1,7 @@
import { conn } from './connect';
import * as assert from 'assert';
import { Fin, Notification } from '../src/main';
+import { cleanOpenRuntimes } from './multi-runtime-utils';
// tslint:disable-next-line
describe('Notification', function () {
@@ -8,7 +9,8 @@ describe('Notification', function () {
let notification: Notification;
// tslint:disable-next-line
this.timeout(30000);
- before(() => {
+ before(async() => {
+ await cleanOpenRuntimes();
return conn().then(_fin => {
fin = _fin;
notification = fin.Notification.create({url: 'http://openfin.co'});
diff --git a/test/plugin.test.ts b/test/plugin.test.ts
index 0e0ebf73..401f9e3c 100644
--- a/test/plugin.test.ts
+++ b/test/plugin.test.ts
@@ -1,22 +1,20 @@
import { conn } from './connect';
import { Fin } from '../src/main';
+import { cleanOpenRuntimes } from './multi-runtime-utils';
describe('Plugin.', () => {
let fin: Fin;
- const plugin = {
- name: 'plugin_1',
- version: '0.0.1'
- };
- before(() => {
- return conn().then((res) => fin = res);
+ before(async () => {
+ await cleanOpenRuntimes();
+ fin = await conn();
});
describe('import()', () => {
it('Doesn\'t work in Node environment', async () => {
try {
- await fin.Plugin.import(plugin);
+ await fin.Plugin.import('plugin_1');
} catch (error) {
return true;
}
diff --git a/test/port-discovery.test.ts b/test/port-discovery.test.ts
index 70b838b5..b2642449 100644
--- a/test/port-discovery.test.ts
+++ b/test/port-discovery.test.ts
@@ -3,11 +3,11 @@ import Launcher from '../src/launcher/launcher';
import * as assert from 'assert';
import * as fs from 'fs';
import { connect as rawConnect, launch } from '../src/main';
-import { promiseMap } from '../src/launcher/util';
+import { promiseMap } from '../src/util/promises';
import { ConnectConfig } from '../src/transport/wire';
import { kill, killByPort } from './multi-runtime-utils';
-import { clean } from './connect';
import { delayPromise } from './delay-promise';
+import { cleanOpenRuntimes } from './multi-runtime-utils';
import * as path from 'path';
// tslint:disable-next-line
const appConfig = JSON.parse(fs.readFileSync('test/app.json').toString());
@@ -16,7 +16,9 @@ describe.skip('PortDiscovery.', function () {
// do NOT use => function here for 'this' to be set properly
// tslint:disable-next-line
this.timeout(60000);
- before(clean);
+ before(async () => {
+ return await cleanOpenRuntimes();
+ });
let spawns = 0;
function makeConfig(config: any = {}): ConnectConfig {
const defaultRconfig = {
diff --git a/test/system.test.ts b/test/system.test.ts
index bed1e6e8..4eab9d1f 100644
--- a/test/system.test.ts
+++ b/test/system.test.ts
@@ -1,14 +1,16 @@
import { conn } from './connect';
import { Fin } from '../src/main';
import * as assert from 'assert';
+import { cleanOpenRuntimes } from './multi-runtime-utils';
describe('System.', function () {
let fin: Fin;
// tslint:disable-next-line
this.timeout(30000);
- beforeEach(() => {
- return conn().then((a: Fin) => fin = a);
+ before(async () => {
+ await cleanOpenRuntimes();
+ fin = await conn();
});
describe('getVersion()', () => {
diff --git a/test/window-event.test.ts b/test/window-event.test.ts
index 0ac54c1e..f7a5967a 100644
--- a/test/window-event.test.ts
+++ b/test/window-event.test.ts
@@ -3,6 +3,7 @@ import { conn } from './connect';
import { delayPromise } from './delay-promise';
import * as assert from 'assert';
import { Fin } from '../src/main';
+import { cleanOpenRuntimes } from './multi-runtime-utils';
// tslint:disable-next-line:no-function-expression
describe('Window.', function() {
@@ -21,8 +22,9 @@ describe('Window.', function() {
}
};
- before(() => {
- return conn().then(a => fin = a);
+ before(async () => {
+ await cleanOpenRuntimes();
+ fin = await conn();
});
describe('"closed"', () => {
diff --git a/test/window.test.ts b/test/window.test.ts
index a8db0632..c834fa04 100644
--- a/test/window.test.ts
+++ b/test/window.test.ts
@@ -2,6 +2,7 @@ import { conn } from './connect';
import * as assert from 'assert';
import { connect as rawConnect, Fin, Application, Window } from '../src/main';
import { delayPromise } from './delay-promise';
+import { cleanOpenRuntimes } from './multi-runtime-utils';
describe('Window.', function() {
let fin: Fin;
@@ -19,8 +20,9 @@ describe('Window.', function() {
// tslint:disable-next-line
this.timeout(30000);
- before(() => {
- return conn().then(a => fin = a);
+ before(async () => {
+ await cleanOpenRuntimes();
+ fin = await conn();
});
beforeEach(() => {
diff --git a/tutorials/Plugin.import.md b/tutorials/Plugin.import.md
index febbdb80..1a2bbca5 100644
--- a/tutorials/Plugin.import.md
+++ b/tutorials/Plugin.import.md
@@ -1,16 +1,11 @@
-Imports an OpenFin plugin. Plugins can be written using ES modules, and the API object that
+Imports an OpenFin plugin. Plugins can be written using ES modules, and the API object that
is resolved in the promise contains the exported API of the plugin.
### Example
```js
// This plugin must be listed in root application's manifest
-const plugin = {
- name: 'foo',
- version: '0.0.1'
-};
-
-fin.desktop.Plugin.import(plugin)
+fin.desktop.Plugin.import('foo')
.then((api) => {
api.bar();
})