import Controller from '@ember/controller';
import {
getRootViews,
getChildViews,
getViewBounds,
getViewClientRects,
getViewBoundingClientRect,
} from 'ember-views';
import { moduleFor, ApplicationTest, RenderingTest } from '../../utils/test-case';
import { Component } from '../../utils/helpers';
moduleFor(
'View tree tests',
class extends ApplicationTest {
constructor() {
super(...arguments);
this.addComponent('x-tagless', {
ComponentClass: Component.extend({
tagName: '',
}),
template: '
[{{id}}] {{#if isShowing}}{{yield}}{{/if}}
',
});
this.addComponent('x-toggle', {
ComponentClass: Component.extend({
isExpanded: true,
click() {
this.toggleProperty('isExpanded');
return false;
},
}),
template: '[{{id}}] {{#if isExpanded}}{{yield}}{{/if}}',
});
let ToggleController = Controller.extend({
isExpanded: true,
actions: {
toggle: function() {
this.toggleProperty('isExpanded');
},
},
});
this.add('controller:application', ToggleController);
this.addTemplate(
'application',
`
{{x-tagless id="root-1"}}
{{#x-toggle id="root-2"}}
{{x-toggle id="inner-1"}}
{{#x-toggle id="inner-2"}}
{{x-toggle id="inner-3"}}
{{/x-toggle}}
{{/x-toggle}}
Toggle
{{#if isExpanded}}
{{x-toggle id="root-3"}}
{{/if}}
{{outlet}}
`
);
this.add(
'controller:index',
ToggleController.extend({
isExpanded: false,
})
);
this.addTemplate(
'index',
`
{{x-tagless id="root-4"}}
{{#x-toggle id="root-5" isExpanded=false}}
{{x-toggle id="inner-4"}}
{{#x-toggle id="inner-5"}}
{{x-toggle id="inner-6"}}
{{/x-toggle}}
{{/x-toggle}}
Toggle
{{#if isExpanded}}
{{x-toggle id="root-6"}}
{{/if}}
`
);
this.addTemplate(
'zomg',
`
{{x-tagless id="root-7"}}
{{#x-toggle id="root-8"}}
{{x-toggle id="inner-7"}}
{{#x-toggle id="inner-8"}}
{{x-toggle id="inner-9"}}
{{/x-toggle}}
{{/x-toggle}}
{{#x-toggle id="root-9"}}
{{outlet}}
{{/x-toggle}}
`
);
this.addTemplate(
'zomg.lol',
`
{{x-toggle id="inner-10"}}
`
);
this.router.map(function() {
this.route('zomg', function() {
this.route('lol');
});
});
}
['@test getRootViews']() {
return this.visit('/')
.then(() => {
this.assertRootViews(['root-1', 'root-2', 'root-3', 'root-4', 'root-5']);
this.runTask(() => this.$('#toggle-application').click());
this.assertRootViews(['root-1', 'root-2', 'root-4', 'root-5']);
this.runTask(() => {
this.$('#toggle-application').click();
this.$('#toggle-index').click();
});
this.assertRootViews(['root-1', 'root-2', 'root-3', 'root-4', 'root-5', 'root-6']);
return this.visit('/zomg/lol');
})
.then(() => {
this.assertRootViews(['root-1', 'root-2', 'root-3', 'root-7', 'root-8', 'root-9']);
return this.visit('/');
})
.then(() => {
this.assertRootViews(['root-1', 'root-2', 'root-3', 'root-4', 'root-5', 'root-6']);
});
}
assertRootViews(ids) {
let owner = this.applicationInstance;
let actual = getRootViews(owner)
.map(view => view.id)
.sort();
let expected = ids.sort();
this.assert.deepEqual(actual, expected, 'root views');
}
['@test getChildViews']() {
return this.visit('/')
.then(() => {
this.assertChildViews('root-2', ['inner-1', 'inner-2']);
this.assertChildViews('root-5', []);
this.assertChildViews('inner-2', ['inner-3']);
this.runTask(() => this.$('#root-2').click());
this.assertChildViews('root-2', []);
this.runTask(() => this.$('#root-5').click());
this.assertChildViews('root-5', ['inner-4', 'inner-5']);
this.assertChildViews('inner-5', ['inner-6']);
return this.visit('/zomg');
})
.then(() => {
this.assertChildViews('root-2', []);
this.assertChildViews('root-8', ['inner-7', 'inner-8']);
this.assertChildViews('inner-8', ['inner-9']);
this.assertChildViews('root-9', []);
this.runTask(() => this.$('#root-8').click());
this.assertChildViews('root-8', []);
return this.visit('/zomg/lol');
})
.then(() => {
this.assertChildViews('root-2', []);
this.assertChildViews('root-8', []);
this.assertChildViews('root-9', ['inner-10']);
return this.visit('/');
})
.then(() => {
this.assertChildViews('root-2', []);
this.assertChildViews('root-5', []);
this.runTask(() => this.$('#root-2').click());
this.runTask(() => this.$('#inner-2').click());
this.assertChildViews('root-2', ['inner-1', 'inner-2']);
this.assertChildViews('inner-2', []);
});
}
['@test getChildViews does not return duplicates']() {
return this.visit('/').then(() => {
this.assertChildViews('root-2', ['inner-1', 'inner-2']);
this.runTask(() => this.$('#root-2').click());
this.runTask(() => this.$('#root-2').click());
this.runTask(() => this.$('#root-2').click());
this.runTask(() => this.$('#root-2').click());
this.runTask(() => this.$('#root-2').click());
this.runTask(() => this.$('#root-2').click());
this.runTask(() => this.$('#root-2').click());
this.runTask(() => this.$('#root-2').click());
this.runTask(() => this.$('#root-2').click());
this.runTask(() => this.$('#root-2').click());
this.assertChildViews('root-2', ['inner-1', 'inner-2']);
});
}
assertChildViews(parentId, childIds) {
let parentView = this.viewFor(parentId);
let childViews = getChildViews(parentView);
let actual = childViews.map(view => view.id).sort();
let expected = childIds.sort();
this.assert.deepEqual(actual, expected, `child views for #${parentId}`);
}
viewFor(id) {
let owner = this.applicationInstance;
let registry = owner.lookup('-view-registry:main');
return registry[id];
}
}
);
let hasGetClientRects, hasGetBoundingClientRect;
let ClientRectListCtor, ClientRectCtor;
(function() {
if (document.createRange) {
let range = document.createRange();
if (range.getClientRects) {
let clientRectsList = range.getClientRects();
hasGetClientRects = true;
ClientRectListCtor = clientRectsList && clientRectsList.constructor;
}
if (range.getBoundingClientRect) {
let clientRect = range.getBoundingClientRect();
hasGetBoundingClientRect = true;
ClientRectCtor = clientRect && clientRect.constructor;
}
}
})();
moduleFor(
'Bounds tests',
class extends RenderingTest {
['@test getViewBounds on a regular component'](assert) {
let component;
this.registerComponent('hi-mom', {
ComponentClass: Component.extend({
init() {
this._super(...arguments);
component = this;
},
}),
template: `Hi, mom!
`,
});
this.render(`{{hi-mom}}`);
let { parentElement, firstNode, lastNode } = getViewBounds(component);
assert.equal(
parentElement,
this.element,
'a regular component should have the right parentElement'
);
assert.equal(
firstNode,
component.element,
'a regular component should have a single node that is its element'
);
assert.equal(
lastNode,
component.element,
'a regular component should have a single node that is its element'
);
}
['@test getViewBounds on a tagless component'](assert) {
let component;
this.registerComponent('hi-mom', {
ComponentClass: Component.extend({
tagName: '',
init() {
this._super(...arguments);
component = this;
},
}),
template: `Hi, mom !`,
});
this.render(`{{hi-mom}}`);
let { parentElement, firstNode, lastNode } = getViewBounds(component);
assert.equal(
parentElement,
this.element,
'a tagless component should have the right parentElement'
);
assert.equal(
firstNode,
this.$('#start-node')[0],
'a tagless component should have a range enclosing all of its nodes'
);
assert.equal(
lastNode,
this.$('#before-end-node')[0].nextSibling,
'a tagless component should have a range enclosing all of its nodes'
);
}
['@test getViewClientRects'](assert) {
if (!hasGetClientRects || !ClientRectListCtor) {
assert.ok(
true,
'The test environment does not support the DOM API required to run this test.'
);
return;
}
let component;
this.registerComponent('hi-mom', {
ComponentClass: Component.extend({
init() {
this._super(...arguments);
component = this;
},
}),
template: `Hi, mom!
`,
});
this.render(`{{hi-mom}}`);
assert.ok(getViewClientRects(component) instanceof ClientRectListCtor);
}
['@test getViewBoundingClientRect'](assert) {
if (!hasGetBoundingClientRect || !ClientRectCtor) {
assert.ok(
true,
'The test environment does not support the DOM API required to run this test.'
);
return;
}
let component;
this.registerComponent('hi-mom', {
ComponentClass: Component.extend({
init() {
this._super(...arguments);
component = this;
},
}),
template: `Hi, mom!
`,
});
this.render(`{{hi-mom}}`);
assert.ok(getViewBoundingClientRect(component) instanceof ClientRectCtor);
}
}
);