Skip to content

Commit

Permalink
Update docs and specs
Browse files Browse the repository at this point in the history
  • Loading branch information
paulfalgout committed Aug 7, 2018
1 parent ce28e29 commit 1119c4c
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 8 deletions.
6 changes: 5 additions & 1 deletion docs/app.md
Expand Up @@ -128,9 +128,13 @@ console.log(myStartWithParentApp.isRunning());

### App's `restartWithParent`

Calls `stop` then `start` on the child app when the parent app restarts. Default value is `false`.
Default value is `null`.
It can also be defined as a function returning a boolean value.

If `restartWithParent` is `true` the child app will stop and start with the parent when `restart`ing.
If `restartWithParent` is `false` the child app will neither stop nor start with the parent when `restart`ing.
If `restartWithParent` is `null` the child app will respect `startWithParent` and `stopWithParent` when `restart`ing.

```js
var myApp = new Marionette.Toolkit.App();

Expand Down
53 changes: 53 additions & 0 deletions docs/mixins/child-apps.md
Expand Up @@ -5,6 +5,8 @@
## Documentation Index
* [ChildAppsMixin's Lifecycle Settings](#childappsmixins-lifecycle-settings)
* [ChildAppsMixin's `childApps`](#apps-childapps)
* [`regionName` option](#regionname-option)
* [`getOptions` option](#getoptions-option)
* [ChildAppsMixin API](#childappsmixin-api)
* [ChildAppsMixin `buildApp`](#childappsmixin-buildapp)
* [ChildAppsMixin `addChildApp`](#childappsmixin-addchildapp)
Expand Down Expand Up @@ -55,6 +57,57 @@ var myApp = new Marionette.Toolkit.App({
});
```

### `regionName` option

If a `regionName` is passed along with an app configuration, when the app starts
the region of that name will be passed to the child app from the app's view.

```js
const myApp = new Marionette.Toolkit.App({
childApps: {
childName: {
AppClass: MyChildApp,
regionName: 'bar'
}
}
});

const LayoutView = Marionette.View.extend({
template: _.template('<div id="barRegion"></div>'),
regions: { bar: '#barRegion' }
});

myApp.setView(new LayoutView());

const childApp = myApp.startChildApp('childName');

myApp.getRegion('bar') === childApp.getRegion(); // true
```

### `getOptions` option

If `getOptions` is defined with the app configuration, it will loop through the `getOptions` array,
using `getOption` on the parent app and passing the result to the child app when starting.

```js
const myApp = new Marionette.Toolkit.App({
childApps: {
childName: {
AppClass: MyChildApp,
getOptions: ['foo', 'bar']
}
},
foo: 'foo'
});
myApp.bar = 'bar';

const childApp = myApp.startChildApp('childName', { bar: 'bar2', baz: 'baz' });

childApp.getOption('foo'); // "foo"
childApp.getOption('bar'); // "bar2"
childApp.getOption('baz'); // "baz"
```

## ChildAppsMixin API

### ChildAppsMixin `buildApp`
Expand Down
82 changes: 80 additions & 2 deletions test/unit/app-lifecycle-options.spec.js
Expand Up @@ -141,11 +141,11 @@ describe('app-lifecycle-options', function() {
const childApps = {
cA1: {
AppClass: Marionette.Toolkit.App,
stopWithParent: false
restartWithParent: false
}
};

const myApp = new Marionette.Toolkit.App({ childApps: childApps });
const myApp = new Marionette.Toolkit.App({ childApps });

myApp.start();

Expand All @@ -158,5 +158,83 @@ describe('app-lifecycle-options', function() {
expect(startSpy).to.not.be.called;
});
});

describe('and a childApp has not set restartWithParent', function() {
it('should start when startWithParent is true', function() {
const childApps = {
cA1: {
AppClass: Marionette.Toolkit.App,
startWithParent: true
}
};

const myApp = new Marionette.Toolkit.App({ childApps });

myApp.start();

const startSpy = sinon.spy(myApp.getChildApp('cA1'), 'start');

myApp.restart();

expect(startSpy).to.be.calledOnce;
});

it('should not start when startWithParent is false', function() {
const childApps = {
cA1: {
AppClass: Marionette.Toolkit.App,
startWithParent: false
}
};

const myApp = new Marionette.Toolkit.App({ childApps });

myApp.start();

const startSpy = sinon.spy(myApp.getChildApp('cA1'), 'start');

myApp.restart();

expect(startSpy).to.not.be.called;
});

it('should stop when stopWithParent is true', function() {
const childApps = {
cA1: {
AppClass: Marionette.Toolkit.App,
stopWithParent: true
}
};

const myApp = new Marionette.Toolkit.App({ childApps });

myApp.start();

const stopSpy = sinon.spy(myApp.getChildApp('cA1'), 'stop');

myApp.restart();

expect(stopSpy).to.be.calledOnce;
});

it('should not stop when stopWithParent is false', function() {
const childApps = {
cA1: {
AppClass: Marionette.Toolkit.App,
stopWithParent: false
}
};

const myApp = new Marionette.Toolkit.App({ childApps });

myApp.start();

const stopSpy = sinon.spy(myApp.getChildApp('cA1'), 'stop');

myApp.restart();

expect(stopSpy).to.not.be.called;
});
});
});
});
19 changes: 19 additions & 0 deletions test/unit/app.spec.js
Expand Up @@ -241,6 +241,25 @@ describe('App', function() {
});
});

describe('when a view is detached from the app\s region', function() {
it('should delete the shown view', function() {
this.myApp.setRegion(this.region);
this.myApp.showView(this.view);
this.myApp.getRegion().detachView();
expect(this.myApp.getView()).to.be.undefined;
});

it('should remove any listeners to the view', function() {
this.sinon.spy(this.myApp, 'stopListening');

this.myApp.setRegion(this.region);
this.myApp.showView(this.view);
this.myApp.stopListening.reset();
this.myApp.getRegion().detachView();
expect(this.myApp.stopListening).to.have.been.calledWith(this.view);
});
});

describe('when a view in the app\'s region is destroyed', function() {
it('should delete the shown view', function() {
this.myApp.setRegion(this.region);
Expand Down
50 changes: 45 additions & 5 deletions test/unit/mixins/child-apps.spec.js
Expand Up @@ -342,13 +342,21 @@ describe('ChildAppMixin', function() {
cA1: Marionette.Toolkit.App,
cA2: Marionette.Toolkit.App.extend({
onStart(options) {
this.mergeOptions(options, ['region']);
this.mergeOptions(options, ['foo']);
}
}),
cA3: Marionette.Toolkit.App
cA3: Marionette.Toolkit.App,
cA4: {
AppClass: Marionette.Toolkit.App,
regionName: 'region'
},
cA5: {
AppClass: Marionette.Toolkit.App,
getOptions: ['foo', 'bar']
}
};

this.myApp = new Marionette.Toolkit.App({ childApps: childApps });
this.myApp = new Marionette.Toolkit.App({ childApps });
});

it('should start specified childApp', function() {
Expand All @@ -358,9 +366,9 @@ describe('ChildAppMixin', function() {
});

it('should start childApp with options', function() {
this.myChildApp = this.myApp.startChildApp('cA2', { region: 'regionName' });
this.myChildApp = this.myApp.startChildApp('cA2', { foo: 'bar' });

expect(this.myChildApp.getOption('region')).to.eq('regionName');
expect(this.myChildApp.getOption('foo')).to.eq('bar');
});

it('should return childApp instance', function() {
Expand All @@ -370,6 +378,38 @@ describe('ChildAppMixin', function() {

expect(spy.returned(this.myChildApp)).to.be.true;
});

describe('when regionName is defined', function() {
it('should set the region from the app view on the child app', function() {
const view = new Marionette.View({
template: _.template('<div id="region"></div>'),
regions: { region: '#region' }
});

this.myApp.setView(view);

this.myChildApp = this.myApp.startChildApp('cA4');

expect(this.myChildApp.getRegion()).to.equal(this.myApp.getRegion('region'));
});
});

describe('when getOptions is defined', function() {
it('should set the options from the app on the child app', function() {
this.myApp.foo = 'foo';
this.myApp.bar = 'bar';

this.myChildApp = this.myApp.getChildApp('cA5');

const spy = sinon.spy(this.myChildApp, 'start');

this.myApp.startChildApp('cA5', { bar: 'bar2', baz: 'baz' });

expect(spy)
.to.be.calledOnce
.and.calledWith({ foo: 'foo', bar: 'bar2', baz: 'baz', region: undefined });
});
});
});

describe('stopChildApp', function() {
Expand Down

0 comments on commit 1119c4c

Please sign in to comment.