Ember CLI: How to do Asynchronous Model Unit Testing with RESTAdapter - unit-testing

I want to test my models, and their integration with my REST API, using the Ember testing framework which ships with Ember CLI, but requests, using my RESTAdapter settings are not being made in my tests, within my model tests. As it stands, any call to save() on a model will cause all tests following it to not execute:
Here is a test I made to check interaction with the server (REST API):
test "store can be used to send data to server", ->
store = #store()
Ember.run ->
cpanel = store.createRecord "item"
cpanel.save().then((response) =>
equal(response.status, 200)
)
This completely blocks all tests following this one; furthermore, no requests are made to the server, when monitoring the Network tab in Chrome dev tools:
A friend advised me to use the QUnit Async Helper, but when using that, I find that this.store() is undefined (perhaps the Ember QUnit Adapter decided to leave out support for async testing helpers?):
asyncTest "Async creates account on server", ->
expect(2)
store = #store()
Ember.run =>
account = store.createRecord("account", {
siteName: "sample account"
url: "http://url.com"
})
account.save().then((response) =>
equal(response.status, 200)
ok account
start()
)
How can I write an async test for my individual models, and test their integration with my REST API using the Ember QUnit framework in Ember CLI without having to write integration tests?

I'm not sure where status is coming from, the promise of save returns the record, not the response from server.
In order to use start, you must use stop first.
stop();
someAsyncCall(function(){
start();
});
And the store is only injected into routes and controllers, and isn't in scope of your tests. You'll need to use the container to get the store.
store = App.__container__.lookup('store:main');
It'd look something like this:
test("save a record", function(){
var record,
store = App.__container__.lookup('store:main');
stop();
Em.run(function(){
record = store.createRecord('color', {color:'green'});
record.save().then(function(){
start();
equal(record.get('id'), 1);
});
});
});
Example: http://emberjs.jsbin.com/wipo/49/edit

Related

How to mock remote website responses to unit test Puppeteer code with Jest?

I'm implementing a web-scraping script for a website which will collect some useful information.
Script is implemented using Puppeteer library and is basically a set of instructions like:
Start a headless chrome
Open new tab/page
Get some pagination links from the HTML
Open every pagination link found and scrape some information from HTML
I'm looking for some way of testing this functionality. Ideally, what I want to do is to "pre-save" the real HTML responses in test folder and then mock the external website responses (making sure they always are the same). And then assert that collected information is correct.
I'm familiar with several tools which are able to mock endpoints for fetch function in the browser. I'm looking for something similar but for Puppeteer.
So far, the only solution I'm thinking about is to use browser instance as a dependency for my script. And then mock the newPage method of the browser to return a page with custom interceptor. This looks like a lot of work though.
Any other solution for this?
A simple solution is to store the HTML page you want to test (or parts of it) locally and open it in Puppeteer. It is possible to open local HTML websites in Puppeteer. Then the result can be tested in a Javascript test framework like Mocha or Jest.
If you need a real web server for the tests, another option is to use a library like Express to serve local HTML pages as a mock for a web server response. You can find an example in this search engine scraper which contains tests for scraping various search engines.
It is also possible to mock and stub Puppeteer functions like launch, goto and $eval. This approach requires a lot of stubbed methods.
This is something I am playing around with at the moment.
I got this working by setting setRequestInterception:
it('responds', () => {
return (async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setRequestInterception(true);
page.on('request', request => {
// TODO: match request.url()
request.respond({
content: 'application/json',
headers: {"Access-Control-Allow-Origin": "*"},
body: JSON.stringify({foo: 'bar'})
})
});
res = await page.goto('https://example.com');
json = await res.json()
await browser.close();
expect(json).toStrictEqual({"foo": "bar"})
})();
})
This also looks to be a useful tool: https://github.com/jefflau/jest-fetch-mock. Maybe it could be handy to match requests etc
Also see: Best way to intercept XHR request on page with Puppeteer and return mock response

Where do I use Pretender to enable Ember Qunit integration tests with rails-csrf?

We are using rails-csrf in our ember-cli app. The README on rails-csrf says:
Be sure to mock out the call to the csrf server endpoint. Otherwise your tests will fail with
"error while processing route: [route]"
messages in the browser console. For example:
server = new Pretender(function() {
this.get('/csrf', function(request) {
return [200, {"Content-Type": "application/json"},
JSON.stringify({
"authenticity_token": "token"
})
];
});
});
I understand the problem here (our integration tests are indeed showing this error) and I understand how Pretender solves it. I've got Pretender installed via ember-cli-pretender.
What I don't understand is how to make sure this code snippet - configuration for a Pretender mock - is working. I have it installed in the setup block of the integration test module, and it gets called, but the error is still present and the tests still aren't passing.
Here's the current non-working state:
module('Integration test', {
setup: function() {
App = startApp();
var server = new Pretender(function() {
this.get('/our/api/for/csrf', function(request) {
return [200, {"Content-Type": "application/json"},
JSON.stringify({
"authenticity_token": "token"
// I've also tried putting a real token from the server here instead of "token"
})
];
});
});
},
teardown: function() {
Ember.run(App, App.destroy);
}
});
The mock is getting called, but whatever it's returning is apparently not enough for rails-csrf. It looks like the beforeModel call in the application route is returning a promise; it's not clear if that's expected and being resolved.
(This question is superficially similar to this older one, but the available tools for handling this in Ember have changed significantly.)
I updated our app from ember-cli 0.1.12 and ember 1.8.1 to ember-cli 0.2.0 and ember 1.10.0. I also updated Pretender to 0.6.0 (the ember-cli-pretender package installed 0.1.0). This didn't solve anything by itself, but it did lead to a telling change in how the integration test failed. Now, Pretender was intercepting data requests and returning an error because I hadn't defined handlers for them.
Error: Pretender intercepted GET /our/api/data/:id but no handler was defined for this type of request
So the issue was no longer Ember but my configuration of Pretender. Once I mocked data requests to the API, we were off and running.
tl;dr make sure you have the latest version of Pretender.

EmberJS and WebSocket | Best Approach?

I have a again which I can't answer for my self properly, maybe because of my lack in expierience with EmberJS.
I have to develop a management interface in EmberJS, using Symfony 2 for the backend, which should act and feel like a desktop application. So far so good, but since alot of people will work with the data inside this application, i would really like to use a WebSocket adapter implementation for EmberJS, since every connected client should always know about changes in entities immediately (or asap). I could write a WebSocket adapter for EmberJS but my problem here is that the WebSocket will do much more then RESTful operations, also the server will send messages without any EmberJS request (e.g. an entity changed and the server broadcasting this change to all clients). That means that i need a "command" structure on top of RESTful operations which, as far as my expierience goes, will not work with a pure DS Adapter.
For example:
Maybe i will trigger a controller method that will send a websocket message like this:
{command: "say", parameters: {message: "Hello guys!"} }
This command is not Entity (DS) related and will never go into the application store.
Another example would be like this:
{command: "load entity", parameters: {type: "Vendor\Bundle\Entity\Type", id: 43} }
Which would load an entity which should be stored in the application store.
Well, as i said, im not that familiar with EmberJS that I could figure out which the best approach could be. Should I bypass the DS Adapter completely and check for "isDirty" and just the push methods after loading entities? I'm happy about any idea you have!
As far as I understand your question, you want to push changes from your backend to your single page app?
You can push custom JSON into your application's store in Ember by using self.store.push('modelName', json). Have a look at the docs for a better undestanding.
So for example if your server sends you JSON via websocket that looks like this
{
- "message": {
"type": "fooModel",
"data": {
... // Model attributes here
}
}
}
you can push the data into your store. The following snippet would work with SocketIO for example:
App.ApplicationRoute = Ember.Route.extend({
activate: function() {
// connect to the websocket once we enter the application route
var socket = window.io.connect('http://localhost:8080');
var self = this;
socket.on('message', function(data){
self.store.push(data.type, data.item);
});
}
});
You can easily modify this snippet to fit your needs.

How do you handle a asyc request made in `Ember.Application.initializer` when writing QUnit integration test?

I am trying to setup ember.js with QUnit to write integration tests.
Following the directions on http://emberjs.com/guides/testing/integration/ I have:
document.write('<div id="ember-testing-container"><div id="ember-testing"></div></div>');
App.rootElement = '#ember-testing';
App.setupForTesting();
App.injectTestHelpers();
module("Integration Tests", {
setup: function() {
App.reset();
}
});
test("root lists first page of posts", function(){
visit("/").then(function() {
equal(find(".post").length, 5, "The first page should have 5 posts");
// Assuming we know that 5 posts display per page and that there are more than 5 posts
});
});
However when I run QUnit I get the following error:
Assertion failed: You have turned on testing mode, which disabled the
run-loop's autorun. You will need to wrap any code with asynchronous
side-effects in an Ember.run
This assertion error is triggered because I am making a http request in the app initializer to check if there is a current user session. This returns a 401 error if there is no valid user. (If I force the app to always return 200 for this request, this assertion error does not occur and the tests continue as expected).
I believe the app and API is behaving correctly by returning a http error for an invalid user, and I don't want to change to response to a 200 just to get my tests working. What do I need to do to get ember and QUnit to handle a http error and assertion? From what I've read I need to wrap something with Ember.run, but I can't figure out what.
Your unit and integration tests should ideally run in isolation from external resources (like an HTTP API), so mocking HTTP requests is the easiest pattern.
To get truly full-stack smoke testing, you'll want to exercise the app with browser automation tools like PhantomJS.

Clear Angular modules in unit testing

I'm doing some multi user testing for my AngularJS controllers, as my users have different roles and settings. The first one always works great, but subsequent attempts never issue attempts for HTTP mock requests, as the controllers are singletons. Is there any way for me to clear out all existing controllers, or even the whole Angular module setup and start it back up again so I can perform these types of tests?
Have you tried creating your controller in a beforeEach function? This way your controller will be recreated for each test.
beforeEach(inject(function ($rootScope, $controller, $location) {
root = $rootScope;
scope = $rootScope.$new();
location = $location;
controller = $controller('AccountInfoController', { $scope: scope, $location:$location, customer: customer });
}));
try this code if you want clear module
angular.module('app',[]);