It seems like the viewstack tied with a list is failing. I'm getting a TypeError: Object [object Object] has no method 'provideDecoratedElement' every other time. Is this just me or have others seen it?
override protected function onRegister():void
{
var menuItems:Array = [];
menuItems.push( new MenuItem("Search","views/search.html") );
menuItems.push( new MenuItem("Box Office","views/boxoffice.html") );
...
menu.menuItemSelected.add( menuItemSelected );
menu.data = menuItems;
menuItemSelected(menuItems[0]);
}
private function menuItemSelected(menuData:MenuItem):void{
viewStack.popView();
var promise:Promise = viewStack.pushView(menuData.url);
promise.then(function(result:AbstractMediator):void{
Window.console.log(menuData.url);
}, function(fault:Object):void{
Window.console.log(fault.toString());
});
}
If the views you are loading have mediators, make sure those mediators either extends from AbstractMediator or implement the interface. provideDecoratedElement is used to hand a Behavior or Mediator a reference to the element which it decorates.
If its not there, you are loading a class that's not a mediator or a behavior as a mediator or behavior.
Related
I am doing a store query in the controller and then passing the result down to a child component.
// Controller
#tracked comment;
#action
async fetchComment() {
const comment = await this.store.query('note', {
filter: {
listing: this.listing.id,
},
});
if (comment && comment.length > 0) {
this.comment = comment.firstObject;
}
}
// Template
<MyComponent #comment={{this.comment}} />
I want to use the arg to populate a tracked property in the child component.
// Component
#tracked comment = this.args.comment;
I found this does not work but works well as a getter. However with a getter I am unable to set this to null when deleting that record.
I have also tried to set the tracked property in the Constructor but that didnt work either.
I suspect this has something to do with passing in a promise or store object in the args because this works fine with static data.
why your code does not work
this code can not work:
#tracked comment = this.args.comment;
this is because comment on the controller is initially undefined but will later bet set to comment.firstObject when the network request is done and the await in your fetchComment function returns.
Generally everythings on args basically always behaves like its #tracked (while more accurate you would describe it as getters). So this usually will just update fine. But the assignment #tracked comment = this.args.comment; only happens once when you create the component, so you no longer depend on updates on args.
why you can not set this.args.comment to null
If you use a getter or directly always use this.args.comment you can not change this reference. This is because this.args is always readonly. you can change objects on this.args.something, but you never can change the reference or primitive value on this.args.
Sidenote: this is only true if the component was called with <AngleBracket /> syntax. For the older {{curly-component}} syntax this is not true. So this does not depend on the component itself but how the component gets called.
you could notify the controller to remove the reference
one good thing to do is to pass down a deleteComment action to the component that basically does something like this.comment = null on the controller. then you use this.args.comment directly or by a getter and you can call this.args.deleteComment() to set comment on the controller to null, which will update anything that uses this.args.comment or a getter that returns this.args.comment.
this is essentially because in your architecture the controller owns the data (because it loads it). so the controller is also responsible to delete it.
if you use ember-data you can check isDeleted
if its a ember-data model (which it probably is if you call this.store) then it has a isDeleted property. you can use this to check if the record is deleted, since ember-data records dont disappear if they get deleted. which is exactly because of problems like this.
how you use another property to shadow a argument
you could do something like this to shadow an argument in your component:
#tracked commentIsDeleted = false;
get comment() {
return this.commentIsDeleted
? null
: this.args.comment;
}
this way at first this.comment will work like a normal getter, but you can shadow delete it by setting this.commentIsDeleted = true;. From that on this.comment will be null.
I'm very new to testing controllers and I'm running into a problem with a method(). I believe I'm either missing something in my test or my Controller / Repository is designed incorrectly.
The application I'm writing is basically one of those secure "one time" tools. Where you create a note, the system provides you with a URL, once that url is retrieved the note is deleted. I actually have the application written but I am going back to write tests for practice (I know that's backwards).
My Controller:
use OneTimeNote\Repositories\NoteRepositoryInterface as Note;
class NoteController extends \Controller {
protected $note;
public function __construct(Note $note)
{
$this->note = $note;
}
public function getNote($url_id, $key)
{
$note = $this->note->find($url_id, $key);
if (!$note) {
return \Response::json(array('message' => 'Note not found'), 404);
}
$this->note->delete($note->id);
return \Response::json($note);
}
...
I've injected my Note interface in to my controller and all is well.
My Test
use \Mockery as M;
class OneTimeNoteTest extends TestCase {
public function setUp()
{
parent::setUp();
$this->mock = $this->mock('OneTimeNote\Repositories\EloquentNoteRepository');
}
public function mock($class)
{
$mock = M::mock($class);
$this->app->instance($class, $mock);
return $mock;
}
public function testShouldReturnNoteObj()
{
// Should Return Note
$this->mock->shouldReceive('find')->once()->andReturn('test');
$note = $this->call('GET', '/note/1234567890abcdefg/1234567890abcdefg');
$this->assertEquals('test', $note->getContent());
}
}
...
The error I'm getting
1) OneTimeNoteTest::testShouldReturnNoteObj
ErrorException: Trying to get property of non-object
/Users/andrew/laravel/app/OneTimeNote/Controllers/NoteController.php:24
Line 24 is in reference to this line found in my controller:
$this->note->delete($note->id);
Basically my abstracted repository method delete() obviously can't find $note->id because it really doesn't exist in the testing environment. Should I create a Note within the test and try to actually deleting it? Or would that be something that should be a model test? As you can see I need help, thanks!
----- Update -----
I tried to stub the repository to return a Note object as Dave Marshall mentioned in his answer, however I'm now receiving another error.
1) OneTimeNoteTest::testShouldReturnNoteObj
BadMethodCallException: Method Mockery_0_OneTimeNote_Repositories_EloquentNoteRepository::delete() does not exist on this mock object
I do have a delete() method in my repository and I know it's working when I test my route in the browser.
public function delete($id)
{
Note::find($id)->delete();
}
You are stubbing the note repository to return a string, PHP is then trying to retrieve the id attribute of a string, hence the error.
You should stub the repository to return a Note object, something like:
$this->mock->shouldReceive('find')->once()->andReturn(new Note());
Building upon Dave's answer, I was able to figure out what my problem is. I wasn't mocking the delete() method. I didn't understand the need to mock each individual method in my controller that would be called.
I just added this line:
$mock->shouldReceive('delete')->once()->andReturnNull();
Since my delete method is just deleting the note after it is found, I went ahead and mocked it but set it to return null.
I have an ExampleModel that calls to an ExampleService that retrieves data from our backend. I can't figure out how to write unit tests for my application; which is structured as shown below:
ExampleService
public function retrieveMyToDoList(parameters):Promise
{
var promise:Promise = performRequest({request: "call to backend", parameters: values, session_id: clientModel.sessionID});
promise.addResultProcessor(parseRetrieveToDoListResult);
return promise;
}
protected function parseRetrieveToDoListResult(data:Object, callback:Function):void
{
does some JSON parsing into an object
callback(null, object containing my retrieved data)
}
ExampleModel
public function getMyToDoList():Promise
{
var promise:Promise = exampleService.retrieveToDoList(parameters);
promise.addResultHandler(onGetToDoListResult);
promise.addErrorHandler(onGetToDoListError);
return promise;
}
private function onGetHeadrsByUserResult(promise:Promise):void
{
// where this event will be listened to by mediators etc
dispatchEvent(new ResponseEvent(GOOD_RESULT));
}
private function onGetHeadrsByUserError(promise:Promise):void
{
dispatchEvent(new ResponseEvent(BAD_RESULT));
}
I'm trying to use asmock to mock my Service so that I can test my Model and how it handles the various results in the resulting Object but how do I mock the callback? I saw examples where the return values were mocked but in my case I'm using the Promise and callback and I'm not too sure how to go ahead.
If someone could please advise.
Thanks!
You can let the mock service return a real promise and call the handleResult method of the promise directly.
FYI: it's not a good idea to have a direct dependency from the model to the service. You should let the service manipulate the model, or pass the results from the service to a command which will manipulate the model. Models should never depend on anything else than helper classes.
I am trying to receive data from the Web Service and I am getting the Data from Web Service back but it is form of [object Object]. Can anybody help me on this.
Below is the code for my web service:
public class WebServiceAccess
{
private var webService:WebService;
private var serviceOperation:AbstractOperation;
private var myValueObjects:ValueObjects;
private var method:String;
[Bindable]
public var employeeData:ArrayCollection;
[Bindable]
public var employees:ArrayCollection;
public function WebServiceAccess(url:String, method:String)
{
webService = new WebService();
this.method = method;
webService.loadWSDL(url);
webService.addEventListener(LoadEvent.LOAD, ServiceRequest);
}
public function ServiceRequest():void
{
serviceOperation = webService.getOperation(method);
serviceOperation.addEventListener(FaultEvent.FAULT, DisplayError);
serviceOperation.addEventListener(ResultEvent.RESULT, DisplayResult);
serviceOperation.send();
}
public function DisplayError(evt:FaultEvent):void
{
Alert.show(evt.fault.toString());
}
public function DisplayResult(evt:ResultEvent):void
{
employeeData = evt.result as ArrayCollection;
Alert.show(employeeData.toString());
}
}
First of all, evt.result is not an ArrayCollection, it is an Object (unless your SOAP service/WSDL are completely screwed up/malformed XML).
Second, you can't just display an Array or ArrayCollection (or generic Object, even) as a String (even though the .toString() method always seems to imply that) anyway, you have to parse the data to get what you want from it.
Now, the WebService class is nice in that it automatically parses the XML file that a SOAP service returns into a single usable Object. So that is actually the hard part.
What you need to do is call various properties of the object to get the data you need.
So if the XML return (look at your WSDL to see what the return should be, I also highly suggest soapUI) is this:
<employee name="Josh">
<start date="89384938984"/>
<photo url="photo.jpg"/>
</employee>
And you wanted to display "Josh" and the photo, you would do this.
var name:String = e.result.employee.name;
var url:String = e.result.employee.photo.url;
It does get more complicated. If the WSDL allows for multiple nodes with the same name at the same level, it does return an ArrayCollection. Then you have to loop through the array and find the exact item you need.
Just remember: The WSDL is god. Period. If it says there can be multiple "employee" nodes, you have to code accordingly, even if you don't see more than one in your tests. The issue is that there always could be multiple nodes.
How do I test which view was rendered from a controller action if what I get is a T4MVC_ActionResult? Under normal circumstances I should be able to directly use TestHelper's methods, like in the examples:
pooController.Details().AssertViewRendered().ForView("Details")
...but, since through T4MVC I get a T4MVC_ActionResult instead of a ViewResult, the part AssertViewRendered<>().ForView("Details") fails. What alternative do I have if I want to test which view was invoked?
UPDATE:
Here's the test code:
[TestMethod]
public void Theme_Controller_Details_Action_Returns_Details_View()
{
var builder = new TestControllerBuilder();
var mockThemeRepository = new Mock<IThemeRepository>();
var themeController = builder.CreateController<Evalgrid.Website.Controllers.ThemeController>(mockThemeRepository.Object);
builder.InitializeController(themeController);
var result = themeController.Details();
result.AssertViewRendered().ForView("Details");
}
I used the debugger setting a breakpoint after the result line, and its variable type is T4MVC_ActionResult, while themeController is Evalgrid.Website.controllers.ThemeController. Note that I have used the fully qualified name of the controller.
I get this:
Expected result to be of type
ViewResult. It is actually of type
T4MVC_ActionResult.
I don't know what's going on.
Actually, T4MVC should not make a difference here. If you directly instantiate your controller and call an action method, you'll get the same thing back whether you use T4MVC or not. i.e. you won't get a T4MVC_ActionResult.
It's only when you write MVC.Foo.Details() that you'll get a T4MVC_ActionResult. That's because MVC.Foo returns an instance of a derived class which does special thing, and not directly your controller class.
Does that make sense?
Update: I'm confused, as looking at the sources for TestControllerBuilder.CreateController, it has:
public T CreateController<T>(params object[] constructorArgs) where T : Controller
{
var controller = (Controller)Activator.CreateInstance(typeof(T), constructorArgs);
InitializeController(controller);
return controller as T;
}
So it's directly instantiating the type that you pass in, which should just call your normal action.
One question about your code: does your Details action method take any parameters? If so, that would explain the problem, as you're calling it with no params, which would be a T4MVC method added in the partial class.