I'm trying to test a component that has an #ViewChild annotation. One of the functions that I'm trying to test calls the #ViewChild's element for focus. However, when I try to log out the #ViewChild variable, it is always undefined. I thought componentFixture.detectChanges() would initiate the ElementRef, but it doesn't seem to.
Is there any way to make it so it isn't undefined?
You didn't show your code but, probably u have that undefined because you did your ViewChild like:
#ViewChild(MySubComponent)
instead of
#ViewChild('componentref')
and then in your template:
<my-sub-component #componentref></my-sub-component>
and of course you need to init your component with componentFixture.detectChanges()
I don't know which version of Angular2 you use and how you initialize your test suite but the detectChanges method on the ComponentFixture instance is responsible to set such fields.
Here is a sample test that shows this:
it('should set testElt', injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
return tcb.createAsync(MyList).then((componentFixture: ComponentFixture) => {
expect(componentFixture.componentInstance.testElt).toBeUndefined();
componentFixture.detectChanges();
expect(componentFixture.componentInstance.testElt).toBeDefined();
var testElt = componentFixture.componentInstance.testElt;
expect(testElt.nativeElement.textContent).toEqual('Some test');
});
}));
See the corresponding plunkr: https://plnkr.co/edit/THMBXX?p=preview.
Related
I have a v-if rendering in my component that is link to a ref in the setup method.
How can I test the rendrering in my testing script.
Example:
In the component:
<div v-if="isCovid" class="covid"/>
setup() {
const isCovid = ref(true);
}
In the test component (I try this but dont work, the test received true):
it('do not render covid waring when its false', () => {
const wrapper = mount(TicketHeader,{
data(){
return { isCovid: false}
}
});
expect(wrapper.find('.covid').exists()).toBe(false)
});
If you are using the setup() method this is what counts. In the test you then use data() which AFAIK doesn't work if you use setup().
One small side note there, do you return isCovid so it is accessible in the
template?
I don't think you can modify a ref that you create in setup(). You'll probably need to provide something as a prop which you then access there. I just found this with an example:
https://lmiller1990.github.io/vue-testing-handbook/composition-api.html#the-component
I'm trying to create units test for my stencil js component, in the compnentWillLoad() method it will do an HTTP request (using rxjs).when I'm run the test getting error ReferenceError: XMLHttpRequest is not defined.But when removing the HTTP request from the componentWillLoad() method test passed.
My test as below,
it('should render my component', async () => {
const page = await newSpecPage({
components: [MyComponent],
html: `<my-component></my-component>`,
});
expect(page.root).toEqualHtml(`<my-component></my-component>`);
});
I'm getting error ReferenceError: XMLHttpRequest is not defined
XMLHttpRequest is indeed not defined in the virtual DOM context that is created when you use newSpecPage.
The best solution for you is probably to write this as an E2E test instead, using newE2EPage, which is more suited for complete end-to-end testing because it runs in a real browser context where XMLHttpRequest will be available.
it('should render', async () => {
const page = await newE2EPage({ html: '<my-component></my-component>' });
const myComponent = page.find('my-component');
expect(myComponent).toHaveClass('hydrated');
});
"Spec Page" testing is rather meant for unit testing components that work stand-alone. If your goal is to actually unit-test your component and you just want to be able to instantiate your component but you don't actually need the request to succeed for testing, then you can also use the Build context from Stencil:
import { Build, ... } from '#stencil/core';
export class MyComponent {
componentWillLoad() {
if (!Build.isTesting) {
// make the request
}
}
// ...
}
I had similar troubles with Stencil, Jest and XMLHttpRequest.
First, make sure you call
new window.XMLHttpRequest()
instead of simply calling
new XMLHttpRequest()
This seems to be neccessary when using jsdom and may already resolve your issue.
It didn't resolve mine though, since I wanted to make sure there are no real API calls going on. So I tried to mock XMLHttpRequest. However, I ran into other issues while building the mock and finally decided to refactor my code to use Fetch API instead of XMLHttpRequest which seems to be better supported by Stencil.
You can easily mock fetch using jest
export function mockFetch(status, body, statusText?) {
// #ts-ignore
global.fetch = jest.fn(() =>
Promise.resolve({
status: status,
statusText: statusText,
text: () => Promise.resolve(JSON.stringify(body)),
json: () => Promise.resolve(body),
})
)
}
I am needing to spyOn window.location.assign for my unit test. But when I run the test I get this error.
Cannot spy the assign property because it is not a function; undefined given instead
Here is my code:
jest.spyOn(window.location, "assign");
Could anyone give me some hints or solutions on this case?
Since Jest v25 (Which uses a newer version of JSDOM) you will get the following error:
TypeError: Cannot assign to read only property 'assign' of object '[object Location]'
This is not a Jest/JSDOM bug by the way. This is normal browser behaviour and JSDOM tries to act like a real browser.
A workaround is to remove the location object, create your own one and after running your tests you should reset it to the original location object:
describe('My awesome unit test', () => {
// we need to save the original object for later to not affect tests from other files
const realLocation = global.location
beforeAll(() => {
delete global.location
global.location = { assign: jest.fn() }
// or even like this if you are also using other location properties (or if TypeScript complains):
// global.location = { ...realLocation, assign: jest.fn() }
})
afterAll(() => {
global.location = realLocation
})
it('should call location.assign', () => {
// ...your test code
expect(global.location.assign).toHaveBeenCalled()
// or even better:
// expect(global.location.assign).toHaveBeenCalledWith('/my_link')
})
})
As window can only be accessed through the global keyword in jest tests and window.location.assign is not implemented in jsdom, you can try
jest
.spyOn(global.location, "assign")
.mockImplementation(url => console.log(url))
I use enzyme with sinon for unit testing React components. Normally, when it comes to testing instance methods I just spy on the method on instance of the component and assert respectively.
However, I have this global function that I use in many components of the app, which is a named export. sinon throws if I try to spy on that.
import { openModel } from '../global/handlers/';
<Block
onRemove={(data) => openModal(...args)}
/>
So, currently I am calling prop method onRemove to assert that openModal gets called with the arguments but I can't really spy on the exported method i.e. openModal.
I understand that I need to provide a context to this function to be able to spy on the underlying function but I am not really sure what's the preferred way of doing something like this.
PS: I would be happy to provide more details if need be.
If you are using webpack to build your test code, then you can use inject-loader to replace the imported module with a stub:
describe('Your component', () => {
let openModalSpy;
let Component;
// Use whatever the path to your component is
const injectImports = require('inject-loader!components/Component');
beforeEach(() => {
openModalSpy = sinon.spy();
Component = injectImports({
openModal: openModalSpy
}).default;
})
it('calls open modal with data argument', () => {
const wrapper = shallow(
<Component />
);
// Do something that will result in openModal being called
expect(openModalSpy).to.have.been.calledWith({
// some data
});
}
}
I'm trying to write a unit test for a controller that uses simple-auth authentication in an ajax call. Assertion tests work great but the session property does not appear to be defined in the unit test module scope.
Example action in controller:
authenticate() {
let credentials = this.getProperties('identification', 'password');
this.get('session').authenticate('simple-auth-authenticator:token', credentials)
.then(() => {
this.transitionToRoute('index');
}, (error) => {
this.set('errorMessage', error.error);
});
}
Example test:
it('should not authenticate', function () {
let controller = this.subject();
controller.send('authenticate');
expect(controller.get('errorMessage')).to.equal("Invalid email/password combination");
});
Session is undefined error message:
TypeError: Cannot read property 'authenticate' of undefined
at authenticate (http://localhost:7357/assets/app.js:587:28)
at mixin.Mixin.create.send (http://localhost:7357/assets/vendor.js:37164:54)
at Context.<anonymous> (http://localhost:7357/assets/app.js:2002:18)
at Context.wrapper (http://localhost:7357/assets/test-support.js:1756:27)
at invoke (http://localhost:7357/assets/test-support.js:13772:21)
at Context.suite.on.context.it.context.specify.method (http://localhost:7357/assets/test-support.js:13837:13)
at Test.require.register.Runnable.run (http://localhost:7357/assets/test-support.js:7064:15)
at Runner.require.register.Runner.runTest (http://localhost:7357/assets/test-support.js:7493:10)
at http://localhost:7357/assets/test-support.js:7571:12
at next (http://localhost:7357/assets/test-support.js:7418:14)
In unit tests you don't have a running application so injections etc. that happen in initializers aren't run. The best way to make sure the session exists in the controller would be to stub it which would also make it easy to make sure it behaves as you want it to behave in your test.
The alternative would be to turn the unit test into an acceptance test - in that case you have an initialized app that the test runs with and the session will be available in the controller already.