Skip to content

Commit

Permalink
Refactor acceptance tests
Browse files Browse the repository at this point in the history
The main goal is to get the tests to more accurately reflect what
actually happen in real applications. Instead of mocking/reopening
the application "port", we now directly replace the adapter with a
`TestAdapter` that faithfully implements the required hooks the
same way that a "real" adapter does.

This allows us to write acceptance tests by only sending/responding
to messages in a way that is, as a far as the inspector app can is
concerned, indistinguishable from the "real" ember debug behaves in
the real world. The new API also allows us to be a bit more precise
in terms of the expectations (messages sent/received the correct
number of times, in the right order, etc).
  • Loading branch information
chancancode committed Dec 18, 2019
1 parent a5a4065 commit 0b541c6
Show file tree
Hide file tree
Showing 28 changed files with 2,057 additions and 1,653 deletions.
7 changes: 3 additions & 4 deletions app/adapters/basic.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* });
* ```
*/
import EmberObject, { computed } from '@ember/object';
import EmberObject from '@ember/object';

import config from 'ember-inspector/config/environment';

Expand All @@ -24,6 +24,7 @@ export default EmberObject.extend({
*/
init() {
this._super(...arguments);
this._messageCallbacks = [];
this._checkVersion();
},

Expand Down Expand Up @@ -85,11 +86,9 @@ export default EmberObject.extend({
when a message from EmberDebug is received
**/
onMessageReceived(callback) {
this._messageCallbacks.pushObject(callback);
this._messageCallbacks.push(callback);
},

_messageCallbacks: computed(function() { return []; }),

_messageReceived(message) {
this._messageCallbacks.forEach(callback => {
callback(message);
Expand Down
12 changes: 3 additions & 9 deletions app/components/app-picker.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
import Component from '@ember/component';
import { observer } from '@ember/object';
import { alias, reads } from '@ember/object/computed';

export default Component.extend({
classNames: ['app-picker'],

apps: alias('port.detectedApplications.[]'),
selectedApp: reads('port.applicationId'),

selectedDidChange: observer('selectedApp', function() {
this.port.set('applicationId', this.selectedApp);
}),
apps: alias('port.detectedApplications'),
selectedAppId: reads('port.applicationId'),

init() {
this._super(...arguments);
Expand All @@ -19,8 +14,7 @@ export default Component.extend({

actions: {
selectApp(applicationId) {
this.set('selectedApp', applicationId);
this.port.send('app-selected', { applicationId });
this.port.selectApplication(applicationId);
}
}
});
1 change: 0 additions & 1 deletion app/controllers/component-tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ export default Controller.extend({
*/
pinnedObjectId: null,
inspectingViews: false,
viewTreeLoaded: false,

/**
* Bound to the search field to filter the component list.
Expand Down
12 changes: 8 additions & 4 deletions app/controllers/deprecations.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import { action, get, computed } from '@ember/object';
import { action, computed } from '@ember/object';
import Controller from '@ember/controller';
import debounceComputed from 'ember-inspector/computed/debounce';
import searchMatch from 'ember-inspector/utils/search-match';

export default Controller.extend({
init() {
this._super(...arguments);
this.deprecations = [];
},

search: null,
searchValue: debounceComputed('search', 300),
toggleDeprecationWorkflow: false,

filtered: computed('model.@each.message', 'search', function() {
return this.model
.filter((item) => searchMatch(get(item, 'message'), this.search));
filtered: computed('deprecations.@each.message', 'search', function() {
return this.deprecations.filter(item => searchMatch(item.message, this.search));
}),

openResource: action(function(item) {
Expand Down
11 changes: 2 additions & 9 deletions app/initializers/setup.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
import { typeOf } from '@ember/utils';
import config from 'ember-inspector/config/environment';
import PromiseAssembler from "ember-inspector/libs/promise-assembler";

export default {
name: 'setup',
initialize(instance) {
// {{EMBER_DIST}} is replaced by the build process.
instance.adapter = '{{EMBER_DIST}}';
// {{EMBER_DIST}} is replaced by the build process (basic, chrome, etc)
let Adapter = instance.resolveRegistration(`adapter:{{EMBER_DIST}}`);

// register and inject adapter
let Adapter;
if (typeOf(instance.adapter) === 'string') {
Adapter = instance.resolveRegistration(`adapter:${instance.adapter}`);
} else {
Adapter = instance.adapter;
}
register(instance, 'adapter:main', Adapter);
instance.inject('controller:deprecations', 'adapter', 'adapter:main');
instance.inject('route:application', 'adapter', 'adapter:main');
Expand Down
5 changes: 5 additions & 0 deletions app/routes/app-detected.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export default Route.extend({
this.applicationBooted = ({ booted }) => {
if (booted) {
port.off('general:applicationBooted', this.applicationBooted);
this.applicationBooted = null;
resolve();
}
};
Expand All @@ -27,6 +28,10 @@ export default Route.extend({
});
},

afterModel() {
this.port.send('deprecation:getCount');
},

/**
* Sets up a listener such that if ember-debug resets, the inspector app also
* resets.
Expand Down
1 change: 0 additions & 1 deletion app/routes/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export default Route.extend({
port.on('objectInspector:droppedObject', this, this.droppedObject);
port.on('deprecation:count', this, this.setDeprecationCount);
port.on('view:inspectComponent', this, this.inspectComponent);
port.send('deprecation:getCount');
},

deactivate() {
Expand Down
24 changes: 13 additions & 11 deletions app/routes/component-tree.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import TabRoute from "ember-inspector/routes/tab";
import { Promise } from 'rsvp';
import TabRoute from 'ember-inspector/routes/tab';

export default TabRoute.extend({
queryParams: {
Expand All @@ -8,21 +9,27 @@ export default TabRoute.extend({
},

model() {
return [];
return new Promise(resolve => {
this.port.one('view:viewTree', resolve);
this.port.send('view:getTree');
});
},

setupController() {
setupController(controller, message) {
this._super(...arguments);
this.setViewTree(message);
},

activate() {
this._super(...arguments);
this.port.on('view:viewTree', this, this.setViewTree);
this.port.on('view:stopInspecting', this, this.stopInspecting);
this.port.on('view:startInspecting', this, this.startInspecting);
this.port.on('view:inspectDOMNode', this, this.inspectDOMNode);

this.set('controller.viewTreeLoaded', false);
this.port.send('view:getTree');
},

deactivate() {
this._super(...arguments);
this.port.off('view:viewTree', this, this.setViewTree);
this.port.off('view:stopInspecting', this, this.stopInspecting);
this.port.off('view:startInspecting', this, this.startInspecting);
Expand All @@ -31,7 +38,6 @@ export default TabRoute.extend({

setViewTree(options) {
this.set('controller.viewTree', options.tree);
this.set('controller.viewTreeLoaded', true);

// If we're waiting for view tree to inspect a component
const componentToInspect = this.get('controller.pinnedObjectId');
Expand All @@ -41,10 +47,6 @@ export default TabRoute.extend({
},

inspectComponent(viewId) {
if (!this.get('controller.viewTreeLoaded')) {
return;
}

this.controller.inspect(viewId);
},

Expand Down
36 changes: 21 additions & 15 deletions app/routes/deprecations.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,39 @@
import { set } from '@ember/object';
import TabRoute from "ember-inspector/routes/tab";
import { Promise } from 'rsvp';
import { setProperties } from '@ember/object';
import TabRoute from 'ember-inspector/routes/tab';

export default TabRoute.extend({
setupController() {
let port = this.port;
port.on('deprecation:deprecationsAdded', this, this.deprecationsAdded);
port.send('deprecation:watch');
model() {
return new Promise(resolve => {
this.port.one('deprecation:deprecationsAdded', resolve);
this.port.send('deprecation:watch');
});
},

setupController(controller, message) {
this._super(...arguments);
this.deprecationsAdded(message);
},

model() {
return [];
activate() {
this._super(...arguments);
this.port.on('deprecation:deprecationsAdded', this, this.deprecationsAdded);
},

deactivate() {
this._super(...arguments);
this.port.off('deprecation:deprecationsAdded', this, this.deprecationsAdded);
},

deprecationsAdded(message) {
let model = this.currentModel;
let { deprecations } = this.controller;

message.deprecations.forEach(item => {
let record = model.findBy('id', item.id);
let record = deprecations.findBy('id', item.id);
if (record) {
set(record, 'count', item.count);
set(record, 'sources', item.sources);
set(record, 'url', item.url);
setProperties(record, item);
} else {
model.pushObject(item);
deprecations.pushObject(item);
}
});
},
Expand All @@ -36,6 +43,5 @@ export default TabRoute.extend({
this.port.send('deprecation:clear');
this.currentModel.clear();
}

}
});
49 changes: 23 additions & 26 deletions app/services/port.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { set } from '@ember/object';
import Evented from '@ember/object/evented';
import Service from '@ember/service';

Expand All @@ -9,53 +10,49 @@ export default Service.extend(Evented, {
this._super(...arguments);

/*
* An array of objects of the form:
* { applicationId, applicationName }
* A dictionary of the form:
* { applicationId: applicationName }
*/
this.detectedApplications = [];
this.detectedApplications = {};
this.applicationId = undefined;
this.applicationName = undefined;

this.adapter.onMessageReceived(message => {
if (message.type === 'apps-loaded') {
message.apps.forEach(app => {
if (!this.detectedApplications.mapBy('applicationId').includes(app.applicationId)) {
this.detectedApplications.pushObject(app);
}
});
}
});

this.adapter.onMessageReceived(message => {
const { applicationId, applicationName } = message;

if (message.type === 'app-list') {
const apps = JSON.parse(message.appList);
apps.forEach((app) => {
if (!this.detectedApplications.mapBy('applicationId').includes(app.applicationId)) {
this.detectedApplications.push(app);
}
message.apps.forEach(({ applicationId, applicationName }) => {
set(this.detectedApplications, applicationId, applicationName);
});

return;
}

let { applicationId, applicationName } = message;

if (!applicationId) {
return;
}

if (!this.applicationId) {
this.set('applicationId', applicationId);
}
// save the application, in case we haven't seen it yet
set(this.detectedApplications, applicationId, applicationName);

// save list of application ids
if (!this.detectedApplications.mapBy('applicationId').includes(applicationId)) {
this.detectedApplications.pushObject({ applicationId, applicationName });
if (!this.applicationId) {
this.selectApplication(applicationId);
}

if (this.applicationId === applicationId) {
this.trigger(message.type, message, applicationId);
}
});
},

selectApplication(applicationId) {
if (applicationId in this.detectedApplications && applicationId !== this.applicationId) {
let applicationName = this.detectedApplications[applicationId];
this.setProperties({ applicationId, applicationName });
this.send('app-selected', { applicationId, applicationName });
}
},

send(type, message) {
message = message || {};
message.type = type;
Expand Down
10 changes: 5 additions & 5 deletions app/templates/components/app-picker.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
class="dropdown__select"
onchange={{action "selectApp" value="target.value"}}
>
{{#each apps as |app|}}
{{#each-in this.apps as |id name|}}
<option
value={{app.applicationId}}
selected={{eq app.applicationId selectedApp}}
value={{id}}
selected={{eq id this.selectedAppId}}
>
{{app.applicationName}}
{{name}}
</option>
{{/each}}
{{/each-in}}
</select>
{{svg-jar "dropdown-arrow" class="dropdown__arrow"}}
</div>
8 changes: 4 additions & 4 deletions ember_debug/adapters/basic.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export default EmberObject.extend({
resolve(this.connect(), 'ember-inspector').then(() => {
this.onConnectionReady();
}, null, 'ember-inspector');

this._messageCallbacks = [];
},

/**
Expand Down Expand Up @@ -54,7 +56,7 @@ export default EmberObject.extend({
@param {Function} callback
*/
onMessageReceived(callback) {
this.get('_messageCallbacks').pushObject(callback);
this._messageCallbacks.push(callback);
},

/**
Expand All @@ -70,10 +72,8 @@ export default EmberObject.extend({
*/
inspectElement(/* elem */) {},

_messageCallbacks: computed(function() { return A(); }),

_messageReceived(message) {
this.get('_messageCallbacks').forEach(callback => {
this._messageCallbacks.forEach(callback => {
callback(message);
});
},
Expand Down
Loading

0 comments on commit 0b541c6

Please sign in to comment.