I am using embedded controllers to generate dynamic content in side templates(eg: a menu)
Usually, I implement functionnal tests to assert controllers.
So far, functionnal tests are passing and phpunit considers my embedded controllers code-covered
I am wondering how to test the embedded controller with different inputs and evaluate the outputs... Is that Unit Testing right ?
I know Unit Testing controller is a bad practice, but how can I function test an embedded controller when there is no Request Object ?
The route/url is something that the Twig render() function takes care of.
{{ render(controller('AppSuperBundle:Default:generateMenu', {'params': ... } )) }}
An example to illustrate:
class DefaultController extends Controller
{
public function testAction()
{
return $this->render('AppSuperBundle::index.html.twig');
}
public function generateMenuAction($route, Array $RouteParams)
{
$repo = $this->getDoctrine()->getRepository(...
//some process to generate params of menu items (eg:locale, url, name...)
return $this->render('AppSuperBundle::menu.html.twig', array('menuItems' => $menuItemsWithParams));
}
}
The template index.html.twig
<html>
<body>
{% block menu %}
{{ render(controller('AppSuperBundle:Default:generateMenu', {'route': app.request.attributes.get('_route'), 'RouteParams': app.request.attributes.get('_route_params')} )) }}
{% endblock %}
{% block content %}
...
{% endblock %}
</body>
</html>
What are your thoughts on this ?
Your embedded controllers do not exist in a vaccum. They are being loaded by templates used in your main controllers.
I would say that it is sufficient to only check main controllers. If you really want to check different output from embedded controllers just test the main controller with appropriate params. In the end it is the main controller that is pasing different values to your embedded controllers.
Since render a view is the response, and you are talking about unit test,
And i would highly recommend unit test the controllers, since in some projects the controllers can have a lot of logic there.
i would unit test a controller to its behavour,so it won't throw strange error from the code in the controller. so what i suggest you to do is create a test method for each case in each action, and you will probably need to mock some of the objects the controller is using, here is an example:
public function testIndexAction()
{
$this->employeeRepository->expects($this->once())->method('findByFilter')->will($this->returnValue($this->employee));
$this->entityManager->expects($this->once())->method('getRepository')->will(
$this->returnValue($this->employeeRepository)
);
$this->employeeManager->expects($this->once())->method('formatEmployeeData')->will(
$this->returnValue($this->formattedJson)
);
$this->mockContainer($this->object);
$this->object->indexAction();
}
Related
I am building a simple button component and I would like to test that my click handler is working by passing in console.log (or some other function) into my component. This is in Ember 4.
app/components/eu-button.hbs looks like:
<button
type={{ this.type }}
class={{ this.colors }}
...attributes
{{on "click" (fn #onClick) }}
>
{{#if this.args.icon }}<FaIcon #icon={{ this.args.icon }} />{{/if}}
{{ this.args.text }}
{{ yield }}
</button>
and implementation is:
import Component from '#glimmer/component';
export default class EuButtonComponent extends Component {
get type() { return "button"; }
get colors() { return "various classes"; }
}
I am calling it from my app/templates/application.hbs like this:
<EuButton #text="Test" #icon="pencil" #onClick={{ fn console.log "test" }}/>
In the hopes that I could see the console print the word "test" on a button click. However, I'm getting:
Uncaught Error: Attempted to resolve a value in a strict mode template, but that value was not in scope: console
I have tried passing in #onClick={{ fn window.console.log "test" }} and #onClick={{ fn document.console.log "test" }} with similar errors.
I think my error is more a misunderstanding of JS that Ember (or Glimmer) so I'd appreciate any help on understanding that function's scope or, alternately, a function I could use in place of console.log in this way.
I don't think you can do it from the .hbs file. But if that's not your goal (which i doubt it is) then you can add a function called an action to your controller application.js (or wherever you call the component from; might be a containing component) file and there you'll be able to do whatever you want.
import Controller from '#ember/controller';
// add this decorator here
import { action } from '#ember/object';
export default class ApplicationController extends Controller {
// here's your handler
#action
onClick(message) {
console.log(message);
}
}
then you'll be able to call it from the .hbs:
<EuButton #text="Test" #icon="pencil" #onClick={{ fn this.onClick "test" }}/>
Relevant reading: https://guides.emberjs.com/release/components/component-state-and-actions/#toc_html-modifiers-and-actions or even better https://guides.emberjs.com/release/in-depth-topics/patterns-for-actions/
The rest of the blade files are reading values from the controller well. The ..layout/app.blade file is getting undefined variable $names from ..Layout/App.php.
Below is my App.php. I have tried to dd($names) seems its the App.php is not being reached.
<?php
namespace App\Http\Livewire\Layouts;
use Livewire\Component;
class App extends Component
{
public $names ="Alex Boey";
public function mount(){
dd($this->names);
}
public function render()
{
return view('livewire.layouts.app');
}
}
app.blade.php
<head>
#livewireStyles
</head>
<body>
<div>{{$names}}</div>
{{ $slot }}
#livewireScripts
</body>
View Image to see the files on IDE
A Livewire component can only have a single root element. You're using Livewire to load in the full app view. That simply won't work.
lostika already provided the answer you're looking for;
Replace the <div>{{$names}}</div> in your app.blade with <livewire:layouts.app/>, and then inside the view of your Livewire component:
<div>
{{$names}}
</div>
If the app.blade that you posted is the same as your Livewire component's view, then you need to move that to a separate, non-Livewire location.
This is my code so far:
<Mutation mutation={addUserQuery}>
{
(addUser, data)=>{
console.log(data)
return (
<div className="form">
<form onSubmit={(e)=>{
e.preventDefault();
console.log(e);
addUser({variables: {username: "AuraDivitiae",
firstname: "Michael",
lastname: "Lee"}})
}}>
<button type="submit">Add User</button>
</form>
</div>
)
}
}
</Mutation>
What does Apollo do when a mutation component mounts?
I feel like I don't really understand the processes running inside Apollo.
Does Apollo subscribe to the result of the mutation query?
Does it then update the cache on returning?
Is Data then stored in some components state?
I feel like the documentation doesn't provide enough information sometimes...
<Mutation/> component is ... a normal react component - it has own state, lifecycles, it's using apollo client (and its cache), keeps data.
It's probably a bit confusing that being in render we have rerenderings not caused by setState of our component.
If <Mutation/> is a component then de facto your inner content is rendered by render function of <Mutation/>, not in our component (it only renders <Mutation/> component). This is an additional depth level in components tree structure (with own lifecycles).
I am using ember-cli qunit testing using moduleForComponent. I have the following select element inside an ember component I have created.
{{input site as="select"
collection="sites"
selection="site"
optionLabelPath="content.siteLabel"
optionValuePath="content.id"
prompt="Please Select"
label=" "
}}
The actual sites collection is found using the store.
sites : function() {
return this.get('store').find('site');
}.property()
I am using jsMockito to mock out the store.
var siteMock = mock(DS.Model);
when(siteMock).get('id').thenReturn(1);
when(siteMock).get('siteLabel').thenReturn('Qunit');
var storeMock = mock(DS.Store);
when(storeMock).find('site').thenReturn([siteMock]);
I pass this into the components as a parameter in the test.
var component = this.subject({
store : storeMock
});
The generated html looks like this, it seems that siteMock has rendered but the optionLabelPath and optionValuPath did not work correctly even though I have added the appropriate expectations on the mock.
<select id="ember473" class="ember-view ember-select">
<option value="">Please Select</option>
<option id="ember491" class="ember-view" value=""></option>
</select>
I have tested using the getters on the siteMock in the debugger and everything is working as expected. I guess I need another when condition on some property of siteMock but I am not sure what. Can anyone give me some advice on getting this working?
The problem seems to be that you are using content. in your paths optionLabelPath="content.siteLabel" which is thinking about controller proxing to a model.
But your test are using the models directly -undecorated by a controller- and they do not have a content property.
I'm trying to work out how blocks work with ember components. With the following I'd expect project.name to be rendered for each loop.
// components/block-test.js
export default Ember.Component.extend({});
// index.hbs
{{#each project in projects}}
{{#block-test}}
{{project.name}}
{{/block-test}}
{{/each}}
But when I use this pattern project.name is not rendered. Is this the expected behaviour and if so how could I change it to get the above code to work?
component's are intentionally isolated, pass in anything you want to use (you don't need yield if you are passing it in)
{{#block-test project=project}}
--{{project.name}}--
{{/block-test}}
No Component template
--apple--
--dog--
--piano--
With yield
Component template
--{{yield}}--
Using Component
{{#block-test}}
{{project.name}}
{{/block-test}}
--apple--
--dog--
--piano--