How to setup test cases for SAPUI5/OPENUI5 applications? - unit-testing

I try to atomize my tests for a given UI5 application. Therefore I created a folder under WebContent called test-resources. In that folder I placed two files for first tests.
First file: experimental.qunit.html that contains some first working unit test code.
Second file: experimental.opa.html which contains an example code from the documentation.
The testing part looks like this:
opaTest("Should find a Button with a matching property", function(Given, When, Then) {
// Act
Given.iStartMyAppInAFrame("index.html");
When.waitFor({
viewName : "view.master.Master",
controlType : "sap.m.Button",
matchers : new sap.ui.test.matchers.PropertyStrictEquals({
name : "icon",
value : "sap-icon://show"
}),
success : function (aButtons) {
debugger;
ok(true, "Found the button: " + aButtons[0]);
},
errorMessage : "No button with property icon equal to sap-icon://show"
});
Then.waitFor({
// not implemented
});
Then.iTeardownMyAppFrame();
});
First of all I assume that I can search a button also with icon property.
Second assumption is, that viewName is the name and folder of the view file? In the app, the view is the master view of a split app.
I start the test like this:
* In Eclipse mark the project and choose run as "Web App Preview"
* Than of course I see my normal app
* I replace the index.html part with test-resoruces/experimental.opa.html
* Now I can see the test and my app is shown in an iframe
But:
1. The button selection is not working, anyone an idea what's wrong?
2. If I change the html code I have to restart the "Web App Preview" all the time, a reload seems not working. Is there a "better" way to run the tests after updating test code?
The app itself is defined as a component, and the main view is a SplitApp xml file that contains nothing than:
<mvc:View
xmlns:mvc="sap.ui.core.mvc"
displayBlock="true"
xmlns="sap.m">
<SplitApp id="idAppControl" />
</mvc:View>
Meanwhile I detect the problem and fixed it. My syntax of PropertyStrictEquals was wrong.
The restart problem (of Web App Preview) still exists.
I also detected a helpful example:
https://openui5beta.hana.ondemand.com/test-resources/sap/m/demokit/cart/test/BuyProductJourney.qunit.html
It is mentioned here:
http://scn.sap.com/community/developer-center/front-end/blog/2014/10/16/javascript-based-integration-tests-for-sapui5-apps-with-opa

(Have a look at the testing Tutorial in the developper guide)
First of all, in your example you are mixing the levels of abstraction. Directly in your jurney (the order of steps for your tests) there should not be any code like waitFor(), because that is page specific code. So you should create pages, on where your actual arrangements, actions and assertions take place. In the jurney you only call them. like this (source):
opaTest("Should see the post page when a user clicks on an entry of the list", function (Given, When, Then) {
// Arrangements
Given.iStartMyApp();
//Actions
When.onTheWorklistPage.iPressOnTheItemWithTheID("PostID_15");
// Assertions
Then.onThePostPage.theTitleShouldDisplayTheName("Jeans");
});
Those objects onTheWorklistPage and onThePostPage are your actual test steps, wher you search for objects and trigger a click or check the displayed text you create them like that:
Opa5.createPageObjects({
onTheWorklistPage: {
baseClass: Common,
actions: {...},
assertions: {...}
}
})
Now in those actions and assertions you put your waitFor() to get elements and do something with them. This function is described in the API
PS: Your question is very unstructured and I am not shure if I answered your question, if not, please comment.

Related

w2ui overlay is not rendered in cypress headless test

Test passes in headed mode, but always fails in headless mode
I am trying to perform a test on the w2ui field of type "list"
Ideally, when we click on this w2ui list element, a drop-down (overlay) is generated with the select options and then we select an option.
But while running the test in headless mode, this drop-down is not generated.
Code To reproduce the issue:-
Code for the webpage:
Link to HTML code
Save the code provided in the above link in the file "test_webpage.html". Place this HTML file in the directory where cypress.json is located.
Code of Cypress Test:
describe('W2UI List Test', function() {
it('Click List Field', function() {
cy.visit('test_webpage.html');
cy.get('.w2ui-select').siblings('.w2ui-field-helper').should('be.visible').click();
cy.wait(2000);
cy.get('#w2ui-overlay tr[index=0]').should('be.visible').click();
});
});
Test Fail ScreenShot
Yes, there is a bug in Cypress currently where certain mouse events are not properly simulated when the Test Runner window is not the active window. This is being worked on here: https://github.com/cypress-io/cypress/issues/1909#issuecomment-395995180 , This is being worked on
In the meantime, you can change your test code to this, for example:
cy.get('.w2ui-select').siblings('.w2ui-field-helper').click()
cy.contains('Barack Obama').click()
Cypressautomatically checks for actionability before clicking, so no need for should('be.visible')

Detect if a new window was opened in an Ember test

I dont know if this is even possible but here is my issue :
I'm doing some javascript testing in my Ember.js application and at some point, I have a button/link that opens a new window to a partner website, when certain conditions are met.
Now I'm looking for a way to test that behaviour in my karma/qunit tests but I haven't the slightest idea of how to do that. Any pointer would be appreciated !
document.write('<div id="test-app"></div>');
App.rootElement = '#test-app';
App.setupForTesting();
App.injectTestHelpers();
module("Services");
test("Access services", function () {
visit("/p/2/i/services").then(function () {
click('#MyService').then(function () {
// What should I do here ?
});
});
});
My first recommendation would be to abstract the window.open functionality into a module, so the app just accesses a single interface when it wants to open a window. It shouldn't care HOW the module does it, its only concern is that it calls the module's window open method with the correct parameters.
The way you confirm that it's calling the window open method with the correct parameters is by using method stubs for your tests by using something like Sinon.js (http://sinonjs.org/). This will allow you to stub your window open method, and run assertions that it was called a certain number of times and with certain parameters. It's a little hard to get set up the first time, but it's definitely a necessary evil to thoroughly testing your app.

Ember.js: How do I prevent this property observer from being hit more than once?

In my controller I have a property and a function observing that property. When the property changes once, the observer is being hit three times - twice with the old data and once with the new data. You can see it happen in the console window of the jsbin I just created:
jsbin
Usage: click one of the books (not the first one), and look in the console window at the results.
In my actual app, the work to be performed requires an asynchronous download. This observer is downloading the wrong content twice and the correct content once because of the three hits. Making this problem more obvious is that the asynchronous responses do not come back in sequence. :)
An interim solution has been to schedule the download code to run later.
I'm not sure why this is happening, but the guides give a way to fix it. They suggest something like this:
bookObserver: function() {
Ember.run.once(this, 'bookWasChanged');
}.observes('book'),
bookWasChanged: function() {
// This will only run once per run loop
}
Personally, I always make the assumption that observers could fire even when I don't want them to. For instance, with this code, I would do the following:
bookObserver: function() {
var book = this.get('book');
var lastBook = this.get('lastBook');
if (book !== lastBook) {
this.set('lastBook', book);
doSomethingWithBook(book);
}
}.observes('book')
This way, even if Ember calls your observer 1000 times, you're only going to do the work you want when the book actually changes.

Why does this Ember.js app work locally but not on jsFiddle.net?

Here's the fiddle. Here's the gist with the contents of my local file.
As you can see, the HTML and JavaScript are identical, and I'm loading identical versions of the jQuery, Handlebars.js, and Ember.js libraries. It works as expected locally, but does not render the application template on jsFiddle.net.
I see the following error in the Web Console:
[19:44:18.202] Error: assertion failed: You must pass at least an object and event name to Ember.addListener # https://github.com/downloads/emberjs/ember.js/ember-latest.js:51
BTW-To test the gist as a local HTML file, make sure to run it behind a web server or your browser won't download the JavaScript libs. If you have thin installed (ruby webserver), go to the directory it's in and run thin -A file start, then navigate to localhost:3000/jsfiddle-problem.html in your browser.
If you set the "Code Wrap" configuration on your fiddle to one of the options other than "onLoad" your application will work. Here is an example.
The reason for this is Ember initializes an application when the jQuery ready event fires (assuming you have not set Ember.Application.autoinit to false). With the jsFiddle "Code Wrap" configuration set to "onLoad" your application is introduced to the document after the jQuery ready event has fired, and consequently after Ember auto-initializes.
See the snippet below from ember-latest, taken on the day of this writing, which documents Ember auto-initialization being performed in a handler function passed to $().ready.
if (this.autoinit) {
var self = this;
this.$().ready(function() {
if (self.isDestroyed || self.isInitialized) return;
self.initialize();
});
}
This was strange - I couldn't get your fiddle working, specifically your {{controller.foo}} call until I disabled autoinit. I am guessing when using jsfiddle the application initialize kicks off before seeing your router. I also noticed with your fiddle the router was not logging any output even when you had enableLogging set to true.
I updated your fiddle to not use autoinit, http://jsfiddle.net/zS5uu/4/. I know a new version of ember-latest was released today, I wonder if anything about initialization changed.

Proper procedure for using sproutcore-routing

Curious about the proper procedure, or at least common procedure for using sproutcore-routing.
In the read me there it shows this basic example for routing:
SC.routes.add(':controller/:action/:id', MyApp, MyApp.route);
I'm assuming that in most cases MyApp.route would call the supplied action on the supplied controller. My question is more about beyond this step how you handle the setup/teardown stuff for an application where you have lots of primary views.
Are people instantiating new controllers when the controller changes as to always start with a clean slate of data and views? Or is it more common/advisable to instantiate all the controllers and such at load and simply use the routing to show/hide primary views?
I suppose the same question goes when bouncing between actions within a controller. Is it proper to do some teardown, especially on bindings/listeners, and then re-establishing them if the action is recalled?
My question may be a little fuzzy, but I'm basically wondering how people handle lots of primary views, and deal with cleanup so stuff doesn't get stale or chew up lots of resources.
I wrote a blog post that describes a method for this: http://codebrief.com/2012/02/anatomy-of-a-complex-ember-js-app-part-i-states-and-routes/
In most Ember and Sproutcore apps and examples I have seen, controllers are instantiated at app initialization. Routes drives state changes in statecharts, where controllers are updated and views are created/destroyed as needed.
I have the following setup.
in my Ember.Application.create() I have the following code:
MyApp.routes = Em.Object.create({
currentRoute: null,
gotoRoute: function(routeParams) {
console.log('MyApp.routes gotoRoute. type: ' + routeParams.type + ' action: ' + routeParams.action + " id: " + routeParams.id);
if (routeParams.type === 'expectedType' && routeParams.action === 'expectedAction' && routeParams.id) {
//find item with ID and load in controller
MyApp.MyController.findItemWithId(routeParams.id);
//Navigate to the correct state
MyApp.stateManager.goToState('stateName');
}
}
})
SC.routes.add(":action/:type/:id", MyApp.routes, 'gotoRoute');
Then, when I click on things that should cause the URL to change I do:
SC.routes.set("location", "show/item/ID-123-123");
Your app should now be listening to changes in the URL and cause the correct action to happen based on the URL-part.
You could probably move the MyApp.MyController.findItemWithId(routeParams.id); to the enter() function of the statechart (if you are using them), but you do need to store that ID somewhere in some controller.