Angular 4 Unit Tests Attributes of child components - unit-testing

I'm upgrading an Angular 2 app to version 4.0.1, and i'm having troubles with some tests.
I'm trying to get the attributes of a child component and test that the value given is correct.
Unfortunately, the attributes seems to be undefined in the test context, even when the component behave correctly in JiT build and AoT build.
The tests only broke after updating to Angular 4.0.1
Template of my parent component :
<div>
<div *ngIf="isComponentEnabled">
<child-component
superBaseUrl="super_url" i18n-superBaseUrl="frk|"
[slug]="slug"
[name]="name"
[age]="age">
</child-component>
.....
</div>
</div>
Test that breaks :
it(`GIVEN parent-component
THEN it should have correct attribute values`, () => {
componentInstance.name = 'Bar';
componentInstance.age = 90;
componentInstance.slug = 'foo';
attributes = specService.queryAttributes(
componentFixture, 'child-component');
console.log('HEYA -> ', specService.queryDebugElement(
componentFixture, 'child-component'));
expect(attributes['superbaseurl']).toBe('super_url');
expect(attributes['i18n-superbaseurl']).toBe('frk|');
expect(attributes['ng-reflect-slug']).toBe('foo');
expect(attributes['ng-reflect-name']).toBe('Bar');
expect(attributes['ng-reflect-age']).toBe('90');
});
Ouput of the breaking test :
Expected undefined to be 'foo'.
Expected undefined to be 'Bar'.
Expected undefined to be '90'.
Console log inside the test :
<child-component _ngcontent-c115="" i18n-superbaseurl="frk|" superbaseurl="super_url">
</child-component>
So i don't understand why in version 4 the attributes are not generated in this context. In version 2.X.X, we had the ng-reflect but it seems that this is no longer the case.
I've also tried to add a basic attribute (toto="toto") in the template, and this one shows up in the console.log of the test.
Does anybody have an idea why the attributes no longer shows up ?
P.S: The specService.queryAttributes is just a wrapper that call detectChanges before querying the attributes.

Found it !
For anyone interested, you have to add every sub components and compile them in version 4.0.1 in your TestBed if you want the attributes to be queryable.
In version 2.X, that was not required.
EDIT
Also, see, https://github.com/angular/angular/issues/15822 because i don't know if this is intended or not.

Related

How to test an HTML attr doesn't exist in jest & enzyme?

I'm trying to write a test to make sure a particular attribute doesn't exist in my output html, however, I'm having trouble figuring out the appropriate way.
I'm using Jest and Enzyme.
The example html that's being tested is...
Material Design
and the lines that do the testing are...
const linkProps = component.find('a').first().props();
expect( linkProps ).not.toHaveProperty('rel');
I'm not sure if the first line is the most efficient way to find the tag, but it's confirmed to be working. The second line, however, fails even though the rel attr doesn't exist in the html.
It fails with...
expect(received).not.toHaveProperty(path)
Expected path: not "rel"
Received value: undefined
When I use toHaveProperty to test that an attribute does exist, it's fine, but what's the appropriate way to test that it doesn't exist?
I've realised that one possible answer is to use prop() and toBe()
If i'm expecting the attribute to undefined, then that's what I put into the toBe function.
const linkTag = component.find('a');
expect( linkTag.prop('rel') ).toBe(undefined);
There might be better answers though, so I'm not marking this one as correct just yet.
If your test title is 'attribute "rel" should not exist', I would follow same instructions in your test, like:
test('attribute "rel" should not exist', () => {
const linkTag = component.find('a');
expect(linkTag).not.toHaveAttribute('rel');
});
Check toHaveAttribute docs here!

How to create unit test for cakephp 3 Flash error in controller

Below is the codecov coverage of my cakephp 3 application. How do I write a unit test that covers flash error?
You'd best perform an assert on the response body. After all, the Flash messages are nothing but text in a div. An example of a success Flash output:
<div class="message success" onclick="this.classList.add('collapse')">Entity saved</div>
A possible assert for any success or error message could look like:
$this->assertResponseContains('<div class="message success"');
$this->assertResponseContains('<div class="message error"');
Or you could copy the entire expected Flash output.
As of CakePHP 3.7 you can use assertFlashMessage and a few other helpers, there are some examples in the manual
For your case, your test code could look something like this:
class EmployeesControllerTest extends TestCase
{
use IntegrationTestTrait;
public function testEmployeeAddErrorMessage()
{
$bad_data = ['bad' => 'data'];
$this->post('/employees/add', $bad_data);
$this->assertFlashMessage('The employee could not be saved. Please, try again.');
}
}

Stencil, Leaflet, unit testing component, gives TypeError: Cannot read property 'deviceXDPI' of undefined

So we are developing a Stenciljs component which wraps leaflet map and adds some additional functionality.
Now obviously we don't want or need to test Leaflet, but instead, just the parts in our wrapper components.
So, using the test examples, we create our tests,
import { LeMap } from "./le-map";
describe("Map component tests", () => {
it("Should build the map component", async () => {
const map = new LeMap();
expect(map).not.toBeNull();
});
});
try and load the components and test the public functions, but we get
TypeError: Cannot read property 'deviceXDPI' of undefined
> 1 | import {Component, Element, Listen, Method, Prop, Watch} from
'#stencil/core';
> 2 | import L from 'leaflet';
| ^
3 |
4 | #Component({
5 | shadow: false,
We believe this message is because the test is trying to render leaflet, and because it's not a true browser, it can't detect a view so throwing this error, so we've tried to mock leaflet in the tests, but still get the same problem.
We're tried to mock the leaflet module by using jest mocking
jest.genMockFromModule('leaflet');
but this made no diffrence
Only idea I've had is to separate the logic from the components, but that feels wrong, as we'd just be doing this for purpose of testing.
Versions in use are: leaflet: 1.3.4, #stencil: 0.15.2, jest: 23.4.2
Any other suggestions?
Further investigation with, thanks to #skyboyer 's suggestions, leads me to this line of the leaflet core browser.js file
leads me to this line of the leaflet core browser.js file
export var retina = (window.devicePixelRatio || (window.screen.deviceXDPI/window.screen.logicalXDPI)) > 1;
But I'm unable to mock the screen property of window as I get the following error
[ts] Cannot assign to 'screen' because it is a constant or a read-only property,
so I try the following.
const screen = {
deviceXDPI:0,
logicalXDPI:0
}
Object.defineProperty(window, 'screen', screen);
Object.defineProperty(window, 'devicePixelRatio', 0);
Same error, completely ignores this, so I try over riding the export.
jest.spyOn(L.Browser,'retina').mockImplementation(() => false);
No joy either, so tried
L.Browser.retina = jest.fn(() => false);
but get it tells me it's a constant and can't be changed (yet the implication stats var so ¯_(ツ)_/¯ )
Anything else I can try?
Further update,
I've managed to mock the window, but this sadly doesn't solve it.
const screenMock = {
deviceXDPI: 0,
logicalXDPI: 0
}
const windowMock = {
value: {
'devicePixelRatio': 0,
'screen': screenMock
}
}
Object.defineProperty(global, 'window', windowMock);
If I console log this, I get the right properties but as soon as I test the instantiation of the component it fails with
TypeError: Cannot read property 'deviceXDPI' of undefined
Reading around it seems Leaflet doesn't check for a DOM and just tries to render anyway, I can't see anyway around this, I saw a leaflet-headless package, but I don't know how I could swap them out just for testing.
Think I will need to look at another strategy for testing, probably protractor.
Found a solution, not fully tested yet, but the tests pass.
I did it by creating a
__mocks__
directory at the same level as the node_modules directory.
created a file called leaflet.js in it. It's a simple file it just contains.
'use strict';
const leaflet = jest.fn();
module.exports = leaflet;
then in my test file (le-map.spec.ts) I just added
jest.mock('leaflet')
before the imports
and now my test passes.
I tried doing this in the test itself but that just gave me the same error, it must be something in the loading sequence which means it has to be manually mocked beforehand.
Hope this helps others, it's been driving me mad for weeks.

React-Intl: access formatMessage in nested component

The question is simple and Im can't believe I cant found a solution (without context) for a situation as simple as this.
Im using react-intl.
I have an App.jsx with a child component Index.jsx with a SideBar.jsx with a CreateNewSomething.jsx.
In every components, the command
<FormattedMessage
id="new_object.fill_title"
defaultMessage='Please fill the "title" parameter'
/>
works perfectly thanks to <IntlProvider> provided by App.jsx
Unfortunally, I can't inject FormattedMessage as state value of a component.
Example:
<Input
s={6}
label={label_dash_title}
style={style_items_form}
onChange={this.onDashTitleChange}
onKeyPress={this.newDashHandleEnterKey}
error= { this.state.title_error }
/>
When the input field is not filled (is required) I want to print an error message.
Unfortunally I can't write:
const error_message = <FormattedMessage
id="new_dash.fill_title"
defaultMessage='Please fill the "title" parameter'
/>
this.setState({"title_error" : error_message});
because I get [object Object] and there is no property inside of it with my translated message.
Searching everywhere, I found I have to use this.props.intl.formatMessage({id: 'new_dash.fill_title'});
but this.props.intl is undefined.
I've tried, in App.jax, to include:
import {addLocaleData, IntlProvider, injectIntl, intlShape} from 'react-intl';
but it makes no difference.
I've tried to pass intl to every child component as props but intl is undefined in App.jsx too.
I don't know where is my mistake.
The module's Wiki explain the method but not how to use.
The solution is simple. You have to change the Export Syntax of your React Component and than this.props.intl will be magically available in the component:
export default injectIntl(MyComponent); //WORKS!!!
Unfortunally:
export default class injectIntl(MyComponent) extends React.Component //DOESN'T WORK

GRAILS: Can I call service logic from _FORM.GSP template?

I'm refactoring scaffolding templates and I hit a wall with this issue:
I was trying to call service (some security logic) from template _FORM.GSP - but in the code part, not in the output part
I've read and tried what was suggested in here: How do I call a Grails service from a gsp?
I've tried to use taglib, but my knowledge of grails may not be extensive enough for that
I've tried add import and def to the beginning of _FORM.GSP file (both grailsApplication and application instantiation of service were crashing on missing property application resp. missing property grailsApplication)
I've even tried to call the taglib from the code both directly as method isAllowedToEdit and also as g.isAllowedToEdit both crashing on unknown method resp. "no such property g"
it seems that template _form.gsp has different rules than standard gsp view
I want to do something like this:
private renderFieldForProperty(p, owningClass, prefix = "") {
boolean hasHibernate = pluginManager?.hasGrailsPlugin('hibernate')
boolean display = true
boolean required = false
if (hasHibernate) {
cp = owningClass.constrainedProperties[p.name]
display = (cp ? cp.display : true)
required = (cp ? !(cp.propertyType in [boolean, Boolean]) && !cp.nullable && (cp.propertyType != String || !cp.blank) : false)
}
/* trying to do this part */
// I want to assign value to cp.editable - so later I can render read-only fields in renderEdit
if (!mySecurityService.canEdit(springSecurityService.currentUser, owningClass.getClass(), actionName, p.name)) {
cp.editable = false
}
/* trying to do this part */
if (display) { %>
<div class="fieldcontain \${hasErrors(bean: ${propertyName}, field: '${prefix}${p.name}', 'error')} ${required ? 'required' : ''}">
<label for="${prefix}${p.name}">
<g:message code="${domainClass.propertyName}.${prefix}${p.name}.label" default="${p.naturalName}" />
<% if (required) { %><span class="required-indicator">*</span><% } %>
</label>
${renderEditor(p)}
</div>
<% } } %>
if there is any way to assign cp.editable - I'll try your suggestions
it seems that template _form.gsp has different rules than standard gsp view
The generated _form.gsp works same as other gsps but the template inside the scr/templates/scaffolding/ is different. Customizing the templates like you are doing is a bit more tricky. Keep in mind that the logic you are writing is for Grails on how to generate views(gsp). Meaning you are telling Grails to check some logic before generating the views in memory or in the file. You might be able to accomplish that to some extend for dynamic (in memory) scaffolding at run-time but for sure not for static scaffolding. That's because Grails
is not aware of currentUser when generating the templates.
Your problem will be much simpler if you generate your views and then customize them instead of modifying their templates. Then you can inject your services and do other checks. However, as you also mentioned those logics are better off in a tag library here.
Also since you mentioned security, rendering a field non-editable does not guaranty inability to edit your fields. I would suggest to put the check logic inside your controller for example in SAVE or UPDATE action to prevent any unauthorized user editing fields.
Did you try this?
<%# page import="com.myproject.MyService" %>
<%
def myService = grailsApplication.classLoader.loadClass('com.myproject.MyService').newInstance()
%>
this will work for sure.
go through this link : click here