Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
263 changes: 263 additions & 0 deletions src/ui/__tests__/ui_app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
import expect from 'expect.js';
import UiApp from '../ui_app.js';
import UiExports from '../ui_exports';
import { isUndefined, noop } from 'lodash';

function getMockSpec(extraParams) {
return {
id: 'uiapp-test',
main: 'main.js',
title: 'UIApp Test',
order: 9000,
description: 'Test of UI App Constructor',
icon: 'ui_app_test.svg',
linkToLastSubUrl: true,
hidden: false,
listed: null,
templateName: 'ui_app_test',
...extraParams
};
}
describe('UiApp', () => {
describe('constructor', () => {
const uiExports = new UiExports({});

it('throw if ID is not given', () => {
function newAppMissingID() {
const spec = {}; // should have id property
const newApp = new UiApp(uiExports, spec);
return newApp;
}
expect(newAppMissingID).to.throwException();
});

describe('defaults', () => {
const spec = { id: 'uiapp-test-defaults' };
let newApp;
beforeEach(() => {
newApp = new UiApp(uiExports, spec);
});

it('use the spec ID', () => {
expect(newApp.id).to.be('uiapp-test-defaults');
});

it('creates fields with undefined value', () => {
// test that the fields exist, but have undefined value
expect('main' in newApp).to.be(true);
expect(isUndefined(newApp.main)).to.be(true);

expect('title' in newApp).to.be(true);
expect(isUndefined(newApp.title)).to.be(true);

expect('description' in newApp).to.be(true);
expect(isUndefined(newApp.description)).to.be(true);

expect('icon' in newApp).to.be(true);
expect(isUndefined(newApp.icon)).to.be(true);

expect('linkToLastSubUrl' in newApp).to.be(true);
expect(isUndefined(newApp.linkToLastSubUrl)).to.be(true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make these assert against undefined instead?

expect(newApp.linkToLastSubUrl).to.be(undefined);

I think this will improve the messages we see in the terminal. As the tests currently are, they will say something like "expected true but got false", which won't give you any insight into what the actual value of the property was. If we change it, the message will be something like "expected undefined but got 'value'".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I deliberately didn't do that because it wouldn't actually assure anything. In JS, a field that was never set on an object evaluates to undefined

> const fooObj = {};
undefined
> fooObj.bar === undefined
true

The in operator was the only way I know of to test that a field has actually been set:

> 'bar' in fooObj
false
> fooObj.bar = 1;
1
> 'bar' in fooObj
true

Copy link
Contributor

@cjcenizal cjcenizal Nov 22, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the docs they give an example making an assertion that something is undefined:

expect(window.r).to.be(undefined);

I think all that matters in this case is that the default values of various properties is undefined when they're undefined by the spec argument. It doesn't matter whether the internal implementation is something like

this.main = this.spec.main;

or

if (this.spec.main) {
  this.main = this.spec.main;
}

});

it('default navLink', () => {
expect(newApp.navLink).to.eql({
id: 'uiapp-test-defaults',
title: undefined,
order: 0,
url: '/app/uiapp-test-defaults',
description: undefined,
icon: undefined,
linkToLastSubUrl: true,
hidden: false,
disabled: false,
tooltip: ''
});
});

it('default order of 0', () => {
expect(newApp.order).to.be(0);
});

it('template name is ui_app', () => {
expect(newApp.templateName).to.be('ui_app');
});
});

describe('with spec', () => {
const spec = getMockSpec();
let newApp;
beforeEach(() => {
newApp = new UiApp(uiExports, spec);
});

it('use the spec ID', () => {
expect(newApp.id).to.be('uiapp-test');
});

it('copies field values from spec', () => {
// test that the fields exist, but have undefined value
expect(newApp.main).to.be('main.js');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small suggestion, but we can make these tests a bit less brittle by asserting against a reference to the spec here, e.g.

expect(newApp.main).to.be(spec.main);

expect(newApp.title).to.be('UIApp Test');
expect(newApp.description).to.be('Test of UI App Constructor');
expect(newApp.icon).to.be('ui_app_test.svg');
expect(newApp.linkToLastSubUrl).to.be(true);
expect(newApp.templateName).to.be('ui_app_test');
expect(newApp.order).to.be(9000);
expect(newApp.navLink).to.eql({
id: 'uiapp-test',
title: 'UIApp Test',
order: 9000,
url: '/app/uiapp-test',
description: 'Test of UI App Constructor',
icon: 'ui_app_test.svg',
linkToLastSubUrl: true,
hidden: false,
disabled: false,
tooltip: ''
});
});
});

describe('reference fields', () => {
const spec = getMockSpec({ testSpec: true });
let newApp;
beforeEach(() => {
newApp = new UiApp(uiExports, spec);
});

it('uiExports', () => {
expect(newApp.uiExports).to.be(uiExports);
});
it('spec', () => {
expect(newApp.spec).to.be(spec);
});
});

describe('getInjectedVars', () => {
it('noop function by default', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another small suggestion, but I like to make sure the message in the terminal forms a grammatically-correct sentence, e.g. by changing this to 'is noop function by default', we will get the message:

UiApp constructor getInjectedVars is noop function by default

vs

UiApp constructor getInjectedVars noop function by default

const spec = {
id: 'uiapp-test'
};
const newApp = new UiApp(uiExports, spec);
expect(newApp.getInjectedVars).to.be(noop);
});
it('reference to spec.injectVars', () => {
const helloFunction = () => 'hello';
const spec = {
id: 'uiapp-test',
injectVars: helloFunction
};
const newApp = new UiApp(uiExports, spec);
expect(newApp.getInjectedVars).to.be(helloFunction);
});
});

describe('hidden and listed', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I was reading these tests, I was having a hard time figuring out how these two properties interacted. After I read the source, I saw that listed was dependent on hidden, and that hidden was cast to a boolean values.

I think this structure makes that relationship a little clearer. What do you think?

    describe('hidden', () => {
      describe('is cast to boolean value', () => {
        it('when undefined', () => {
          const spec = {
            id: 'uiapp-test',
          };
          const newApp = new UiApp(uiExports, spec);
          expect(newApp.hidden).to.be(false);
        });

        it('when null', () => {
          const spec = {
            id: 'uiapp-test',
            hidden: null,
          };
          const newApp = new UiApp(uiExports, spec);
          expect(newApp.hidden).to.be(false);
        });
      });
    });

    describe('listed', () => {
      it('when null defaults to the opposite value of hidden, when hidden is true', () => {
        const spec = {
          id: 'uiapp-test',
          hidden: true,
          listed: null,
        };
        const newApp = new UiApp(uiExports, spec);
        expect(newApp.listed).to.be(false);
      });

      it('when null defaults to the opposite value of hidden, when hidden is false', () => {
        const spec = {
          id: 'uiapp-test',
          hidden: false,
          listed: null,
        };
        const newApp = new UiApp(uiExports, spec);
        expect(newApp.listed).to.be(true);
      });

      it('when undefined defaults to the opposite value of hidden, when hidden is false', () => {
        const spec = {
          id: 'uiapp-test',
          hidden: false,
        };
        const newApp = new UiApp(uiExports, spec);
        expect(newApp.listed).to.be(true);
      });

      it('when undefined defaults to the opposite value of hidden, when hidden is true', () => {
        const spec = {
          id: 'uiapp-test',
          hidden: true,
        };
        const newApp = new UiApp(uiExports, spec);
        expect(newApp.listed).to.be(false);
      });

      it('when true is set to true', () => {
        const spec = {
          id: 'uiapp-test',
          listed: true,
        };
        const newApp = new UiApp(uiExports, spec);
        expect(newApp.listed).to.be(true);
      });

      it('when false is set to false', () => {
        const spec = {
          id: 'uiapp-test',
          listed: false,
        };
        const newApp = new UiApp(uiExports, spec);
        expect(newApp.listed).to.be(false);
      });
    });

it('if hidden and listed are not set, hidden is set as false and listed is set as true', () => {
const spec = {
id: 'uiapp-test'
};
const newApp = new UiApp(uiExports, spec);
expect(newApp.hidden).to.be(false);
expect(newApp.listed).to.be(true);
});
it('if listed is passed as null, and hidden is true, listed is set as false', () => {
const spec = {
id: 'uiapp-test',
hidden: true,
listed: null
};
const newApp = new UiApp(uiExports, spec);
expect(newApp.hidden).to.be(true);
expect(newApp.listed).to.be(false);
});
it('if listed is passed as null, and hidden is false, listed is set as true', () => {
const spec = {
id: 'uiapp-test',
hidden: false,
listed: null
};
const newApp = new UiApp(uiExports, spec);
expect(newApp.hidden).to.be(false);
expect(newApp.listed).to.be(true);
});
it('if listed is passed as null, and hidden not set, listed is set as true', () => {
const spec = {
id: 'uiapp-test',
listed: null
};
const newApp = new UiApp(uiExports, spec);
expect(newApp.hidden).to.be(false);
expect(newApp.listed).to.be(true);
});
it('if listed is passed as true, is set with that value', () => {
const spec = {
id: 'uiapp-test',
listed: true
};
const newApp = new UiApp(uiExports, spec);
expect(newApp.hidden).to.be(false);
expect(newApp.listed).to.be(true);
});
it('if listed is passed as false, is set with that value', () => {
const spec = {
id: 'uiapp-test',
listed: false
};
const newApp = new UiApp(uiExports, spec);
expect(newApp.hidden).to.be(false);
expect(newApp.listed).to.be(false);
});
});
});

describe('getModules', () => {
it('gets modules from uiExports', () => {
const uiExports = new UiExports({});
uiExports.consumePlugin({
uiExportsSpecs: {
chromeNavControls: [ 'plugins/ui_app_test/views/nav_control' ],
hacks: [ 'plugins/ui_app_test/hacks/init' ]
}
});
const spec = getMockSpec();
const newApp = new UiApp(uiExports, spec);

expect(newApp.getModules()).to.eql([
'main.js',
'plugins/ui_app_test/views/nav_control',
'plugins/ui_app_test/hacks/init'
]);
});
});

describe('toJSON', function () {
it('creates plain object', () => {
const uiExports = new UiExports({});
const spec = getMockSpec();
const newApp = new UiApp(uiExports, spec);

expect(newApp.toJSON()).to.eql({
id: 'uiapp-test',
title: 'UIApp Test',
description: 'Test of UI App Constructor',
icon: 'ui_app_test.svg',
main: 'main.js',
navLink: {
id: 'uiapp-test',
title: 'UIApp Test',
order: 9000,
url: '/app/uiapp-test',
description: 'Test of UI App Constructor',
icon: 'ui_app_test.svg',
linkToLastSubUrl: true,
hidden: false,
disabled: false,
tooltip: ''
},
linkToLastSubUrl: true
});
});
});
});
6 changes: 4 additions & 2 deletions src/ui/ui_app.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class UiApp {
this.order = this.spec.order || 0;
this.description = this.spec.description;
this.icon = this.spec.icon;
this.linkToLastSubUrl = this.spec.linkToLastSubUrl;
this.hidden = !!this.spec.hidden;
this.listed = this.spec.listed == null ? !this.hidden : this.spec.listed;
this.templateName = this.spec.templateName || 'ui_app';
Expand All @@ -28,7 +29,8 @@ class UiApp {
order: this.order,
description: this.description,
icon: this.icon,
url: this.spec.url || `/app/${this.id}`
url: this.spec.url || `/app/${this.id}`,
linkToLastSubUrl: this.linkToLastSubUrl
});

if (!this.listed) {
Expand Down Expand Up @@ -63,7 +65,7 @@ class UiApp {
}

toJSON() {
return pick(this, ['id', 'title', 'description', 'icon', 'main', 'navLink']);
return pick(this, ['id', 'title', 'description', 'icon', 'main', 'navLink', 'linkToLastSubUrl']);
}
}

Expand Down