Skip to content
This repository has been archived by the owner on Mar 28, 2023. It is now read-only.

Commit

Permalink
(#330) tooling: added developer tools API
Browse files Browse the repository at this point in the history
  • Loading branch information
Jonas Schulte committed Aug 17, 2016
1 parent 43f59b8 commit 184c602
Show file tree
Hide file tree
Showing 4 changed files with 345 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -2,6 +2,7 @@

## Last Changes

- [#330](https://github.com/LaxarJS/laxar/issues/330): tooling: added developer tools API
- [#322](https://github.com/LaxarJS/laxar/issues/322): logging: fixed source location reporting
- [#296](https://github.com/LaxarJS/laxar/issues/296): documentation: fixed string API doc module name
- [#297](https://github.com/LaxarJS/laxar/issues/297): project: updated .gitignore for better v2.0 compatibility
Expand Down
8 changes: 5 additions & 3 deletions laxar.js
Expand Up @@ -23,7 +23,8 @@ define( [
'./lib/runtime/controls_service',
'./lib/runtime/theme_manager',
'./lib/widget_adapters/adapters',
'./lib/tooling/pages'
'./lib/tooling/pages',
'./lib/tooling/external_api'
], function(
ng,
log,
Expand All @@ -44,7 +45,8 @@ define( [
controlsService,
themeManager,
adapters,
pageToolingApi
pageToolingApi,
externalApi
) {
'use strict';

Expand Down Expand Up @@ -75,7 +77,7 @@ define( [
if( optionalWidgetAdapters && Array.isArray( optionalWidgetAdapters ) ) {
adapters.addAdapters( optionalWidgetAdapters );
}
var dependencies = [ runtime.module.name, runtimeDependencies.name ];
var dependencies = [ runtime.module.name, runtimeDependencies.name, externalApi.name ];

Object.keys( widgetModules ).forEach( function( technology ) {
var adapter = adapters.getFor( technology );
Expand Down
85 changes: 85 additions & 0 deletions lib/tooling/external_api.js
@@ -0,0 +1,85 @@
/**
* Copyright 2016 aixigo AG
* Released under the MIT license.
* http://laxarjs.org/license
*/
define( [
'angular',
'./pages',
'../logging/log',
'../utilities/object',
'../utilities/configuration'
],
function( ng, pages, log, object, configuration ) {
'use strict';

/**
* If the documentElement has the attribute 'data-laxar-developer-tools-extension' or
* the property 'laxarDeveloperToolsExtensionLoaded' of the window is true or
* the tooling is enabled by configuration in the laxar application then
* laxar is providing the API by creating the object `window.laxarDeveloperToolsApi`.
* The attribute 'data-laxar-developer-tools-extension' is set by the web extension and
* the property 'laxarDeveloperToolsExtensionLoaded' is set by the firefox extension.
*/
function create( eventBus ) {
if( !( window.laxarDeveloperToolsExtensionLoaded ||
document.documentElement.hasAttribute( 'data-laxar-developer-tools-extension' ) ||
configuration.get( 'tooling.enabled', false ) ) ) {
return;
}

pages.addListener( onPageChange );
var bufferSize = configuration.get( 'tooling.bufferSize', 2500 );

var developerHooks = window.laxarDeveloperToolsApi = ( window.laxarDeveloperToolsApi || {} );
developerHooks.buffers = { events: [], log: [] } ;
developerHooks.eventCounter = Date.now();
developerHooks.logCounter = Date.now();
developerHooks.pageInfo = pages.current();
developerHooks.pageInfoVersion = 1;
developerHooks.gridSettings = configuration.get( 'tooling.grid', null );

log.addLogChannel( logChannel );
var cleanupInspector = eventBus.addInspector( inspector );

window.addEventListener( 'beforeunload', function() {
log.removeLogChannel( logChannel );
cleanupInspector();
} );

/////////////////////////////////////////////////////////////////////////////////////////////////////

function logChannel( messageObject ) {
var index = developerHooks.logCounter++;
var jsonItem = JSON.stringify( messageObject );
pushIntoStore( 'log', { index: index, json: jsonItem } );
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

function inspector( item ) {
var index = developerHooks.eventCounter++;
var jsonItem = JSON.stringify( object.options( { time: Date.now() }, item ) );
pushIntoStore( 'events', { index: index, json: jsonItem } );
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

function onPageChange( pageInfo ) {
developerHooks.pageInfo = pageInfo;
++developerHooks.pageInfoVersion;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

function pushIntoStore( storeName, item ) {
var buffer = developerHooks.buffers[ storeName ];
while( buffer.length >= bufferSize ) {
buffer.shift();
}
buffer.push( item );
}
}

return ng.module( 'axToolingExternalApi', [] ).run( [ 'axGlobalEventBus', create ] );
} );
256 changes: 254 additions & 2 deletions lib/tooling/spec/tooling_spec.js
Expand Up @@ -4,8 +4,13 @@
* http://laxarjs.org/license
*/
define( [
'../pages'
], function( pageTooling ) {
'angular',
'angular-mocks',
'../pages',
'../external_api',
'../../utilities/configuration',
'../../logging/log'
], function( ng, ngMocks, pageTooling, externalApi, configuration, log ) {
'use strict';

describe( 'The page tooling API', function() {
Expand Down Expand Up @@ -62,6 +67,253 @@ define( [

} );

////////////////////////////////////////////////////////////////////////////////////////////////////////

afterEach( function() {
pageTooling.disable();
} );

} );

///////////////////////////////////////////////////////////////////////////////////////////////////////////

describe( 'The external tooling API', function() {

beforeEach( function() {
pageTooling.enable();
pageTooling.setPageDefinition( 'p', { name: 'my page' }, pageTooling.FLAT );
pageTooling.setCurrentPage( 'p' );
} );

////////////////////////////////////////////////////////////////////////////////////////////////////////

describe( 'with configured tooling enabled ', function() {

var globalEventBus;
var spyInspector;
var spyAddLogChannel;
var testEvent;
var messageObject;

beforeEach( function() {
spyInspector = jasmine.createSpy( 'addInspector' );
spyAddLogChannel = jasmine.createSpy( 'addLogChannel' );
testEvent = {
action: 'testEvent',
source: 'spec test',
target: '-'
};
messageObject = {
id: 0,
level: 'INFO',
text: 'Test logging'
};

spyOn( configuration, 'get' ).andCallFake( function( key, fallback ) {
return key === 'tooling.enabled' ? true : fallback;
} );

spyOn( log, 'addLogChannel' ).andCallFake( function( channel ) {
spyAddLogChannel();
channel( messageObject );
} );

ngMocks.module( externalApi.name, function( $provide ) {
$provide.value( 'axGlobalEventBus', {
addInspector: function( inspector ) {
spyInspector();
inspector( testEvent );
return function(){};
}
} );
} );

ngMocks.inject( function( axGlobalEventBus ) {
globalEventBus = axGlobalEventBus;
} );
} );

/////////////////////////////////////////////////////////////////////////////////////////////////////

afterEach( function() {
window.laxarDeveloperToolsApi = undefined;
} );

/////////////////////////////////////////////////////////////////////////////////////////////////////

it( 'provides the external API', function() {
expect( window.laxarDeveloperToolsApi ).toBeDefined();
} );

/////////////////////////////////////////////////////////////////////////////////////////////////////

it( 'adds an inspector', function() {
expect( spyInspector ).toHaveBeenCalled();
} );

/////////////////////////////////////////////////////////////////////////////////////////////////////

it( 'saves events in the buffer', function() {
var action = JSON.parse( window.laxarDeveloperToolsApi.buffers.events[ 0 ].json ).action;
expect( action ).toEqual( testEvent.action );
} );

/////////////////////////////////////////////////////////////////////////////////////////////////////

it( 'adds a log channel', function() {
expect( spyAddLogChannel ).toHaveBeenCalled();
} );

/////////////////////////////////////////////////////////////////////////////////////////////////////

it( 'saves log messages in the buffer', function() {
var log = window.laxarDeveloperToolsApi.buffers.log[ 0 ];
expect( log.json ).toEqual( JSON.stringify( messageObject ) );
} );

/////////////////////////////////////////////////////////////////////////////////////////////////////

it( 'provides the pageInfo', function() {
expect( window.laxarDeveloperToolsApi.pageInfo ).toEqual( getPageInfo() );
} );

/////////////////////////////////////////////////////////////////////////////////////////////////////

function getPageInfo() {
return {
pageReference : 'p',
pageDefinitions : {
p : {
FLAT : {
name: 'my page'
},
COMPACT : null
}
},
compositionDefinitions : {},
widgetDescriptors : {}
};
}

} );

////////////////////////////////////////////////////////////////////////////////////////////////////////

afterEach( function() {
pageTooling.disable();
} );

////////////////////////////////////////////////////////////////////////////////////////////////////////

describe( 'with configured tooling not enabled', function() {

var globalEventBus;
var spyInspector;
var spyAddLogChannel;

beforeEach( function() {
spyInspector = jasmine.createSpy( 'addInspector' );
spyAddLogChannel = jasmine.createSpy( 'addLogChannel' );

spyOn( configuration, 'get' ).andCallFake( function( key, fallback ) {
return key === 'tooling.enabled' ? false : fallback;
} );

spyOn( log, 'addLogChannel' ).andCallFake( function( channel ) {
spyAddLogChannel();
} );

ngMocks.module( externalApi.name, function( $provide ) {
$provide.value( 'axGlobalEventBus', {
addInspector: function( inspector ) {
spyInspector();
return function(){};
}
} );
} );

ngMocks.inject( function( axGlobalEventBus ) {
globalEventBus = axGlobalEventBus;
} );
} );

/////////////////////////////////////////////////////////////////////////////////////////////////////

it( 'doesn\'t provides the external API', function() {
expect( window.laxarDeveloperToolsApi ).not.toBeDefined();
} );

/////////////////////////////////////////////////////////////////////////////////////////////////////

it( 'doesn\'t add an inspector', function() {
expect( spyInspector ).not.toHaveBeenCalled();
} );

/////////////////////////////////////////////////////////////////////////////////////////////////////

it( 'doesn\'t add a log channel', function() {
expect( spyAddLogChannel ).not.toHaveBeenCalled();
} );

} );

////////////////////////////////////////////////////////////////////////////////////////////////////////

describe( 'when \'window.laxarDeveloperToolsExtensionLoaded\' is true', function() {
var globalEventBus;

beforeEach( function() {
window.laxarDeveloperToolsExtensionLoaded = true;

ngMocks.module( externalApi.name, function( $provide ) {
$provide.value( 'axGlobalEventBus', {
addInspector: function( inspector ) {
return function(){};
}
} );
} );

ngMocks.inject( function( axGlobalEventBus ) {
globalEventBus = axGlobalEventBus;
} );
} );

/////////////////////////////////////////////////////////////////////////////////////////////////////

it( 'provides the external API', function() {
expect( window.laxarDeveloperToolsApi ).toBeDefined();
} );

} );

////////////////////////////////////////////////////////////////////////////////////////////////////////

describe( 'when documentElement has attribute \'data-laxar-developer-tools-extension\'', function() {
var globalEventBus;

beforeEach( function() {
document.documentElement.setAttribute( 'data-laxar-developer-tools-extension', {} );

ngMocks.module( externalApi.name, function( $provide ) {
$provide.value( 'axGlobalEventBus', {
addInspector: function( inspector ) {
return function(){};
}
} );
} );

ngMocks.inject( function( axGlobalEventBus ) {
globalEventBus = axGlobalEventBus;
} );
} );

/////////////////////////////////////////////////////////////////////////////////////////////////////

it( 'provides the external API', function() {
expect( window.laxarDeveloperToolsApi ).toBeDefined();
} );

} );
} );

} );

0 comments on commit 184c602

Please sign in to comment.