BDD Testing angularJS directives - angularjs

I am in a project where we are asked to “componentize” certain controls in angular directives (buttons, checkboxes, tooltip, …)
Very much of what angular-ui is doing…
We would also like to adopt BDD testing like it should. (= start with a BDD set of tests/specs before starting development, see below)
While the project is going further, there is discussion about WHAT part of the directive should be tested WHERE (e2e tests with protractor OR (unit) specs with use of compiled html).
A current “problematic/blurry” directive would be a “tooltip”.
Where do you test what…
The goal of this post should be to get a set of criteria of WHERE to write WHAT kind of tests/specs/whatever/….
A second goal would be to get a correct workflow of developing/testing:
For this I found an idea in this presentation from Julie Ralph
https://docs.google.com/presentation/d/17MoD75c2V26MBvimCMPlZdT-cYm7KZd3mLdng0io258/edit#slide=id.g2570824a9_033
• 1. Logic : Do the controller’s methods do what expected?
• 2. Correct Compilation : Correct DOM structure is generated
• 3. User Event Handling : Is the default tab displaying? Does clicking a tab change the tab?
In my opinion, BDD tests resort under the third (“user event handling”)
Development should start with this “set”.
1 and 2 should “grow” during development.
E2e tests (via protractor) should not by used for one directive, but to test interaction of directives (eg: a button and an input on a form)
Your input is very much appreciated and thank you in advance.

Related

Capybara integration testing 'TextAngular' input

Using TextAngular for a rich text input box in a AngularJS/ Rails environment.
Running integration tests with Capybara/Selenium & Capybara-Webkit.
Tried to create an integration test that inputs text into the text area for a test. However, I have not been able to successfully do this.
The thing that has prevented me is the text input box id changes ever time the test loads or page loads. So I used the below class, which is used in the text angular tests. With:
find('textarea.ta-html.ta-editor')
I used this as i know it works and the javascript tests written for text angular used this. text angular github tests
However, when i try and set the text area with a text value:
find('textarea.ta-html.ta-editor').set("Upgraded to SSD")
I get:
Failure/Error: find('textarea.ta-html.ta-editor').set("Upgraded to SSD")
Selenium::WebDriver::Error::ElementNotVisibleError:
Element is not currently visible and so may not be interacted with
How can I set a value for the text area using Capybara?
The element that matches textarea.ta-html.ta-editor is hidden on the page and is not the element a user interacts with. By design Capybara generally requires you to interact with the elements a user would interact with so you need to call #set on the visible element which is the previous sibling to the element you're attempting to interact with and matches div[contenteditable]. You don't show your HTML but hopefully you have a containing element you can scope to so you can do
find('<container_selector> div[contenteditable]').set(...)
or
find('<more_general_container_selector>', text: '<label text or something else in the container>').find('div[contenteditable]').set(....)
If you only have one of this type of field on the page you can probably get away with
find('.text-angular div[contenteditable]').set('Upgraded to SSD')
Note: If using the selenium driver it has a limitation that it can only interact with the main contenteditable element and not with the children that get created inside it. I'm not sure if the other capybara drivers have the same issue.

Coded UI test fails to find component in WPF application

I'm trying to run a coded UI test on our application. I can record the actions OK, but when attempting to playback, I get
Microsoft.VisualStudio.TestTools.UITest.Extension.UITestControlNotFoundException:
The playback failed to find the control with the given search properties.
Additional Details:
TechnologyName: 'UIA'
FrameworkId: 'Wpf'
ControlType: 'MenuItem'
AutomationId: 'MenuItemConnectId'
When I run Snoop on the application, I see the AutomationId exists
The one odd thing about our application is a lot of the ID's are added with code behind
menuItem.SetValue(AutomationProperties.AutomationIdProperty, "MenuItemConnnectId");
menuItem.SetValue(AutomationProperties.NameProperty, "MenuItemConnect");
Any ideas why this may be failing?
Thanks.
I think there are two reasons:
1.Maybe the component can't find the property.You can see the details in the xxx.Designer.cs file.
In the method SearchProperies[].....
2.The road to search the component is broken.You can see the details in the UI control map.Maybe the the component's parent is wrong.In this situation,you can add the code to connect them.
Turns out I needed the title of the window to be set. Even though I had the Automated ID and name properties set, looks like the engine uses the window's title in order to find it on the desktop. A few more details are here:
MSDN question on Coded UI test fails to find Window (Component)

Are there any tools for recording web browser events for seleniumn2?

I am looking for something very simple. It can use the Selenium IDE recording tool, but it does not allow me to pick what kind of locators I get.
I want to use:
driver.findElement(By.className(str))
to locate things. All I need is something which watches which UI elements on a web page get clicked and writes out the class attributes of those tags.
If I use the Selenium IDE recording (and export to the right type of thing), I get:
#Test
public void testNav() throws Exception {
driver.get(baseUrl + "/");
driver.findElement(By.name("3.1.1.5.1.1")).clear();
driver.findElement(By.name("3.1.1.5.1.1")).sendKeys("dan");
driver.findElement(By.name("3.1.1.5.1.5")).click();
driver.findElement(By.linkText("Products")).click();
driver.findElement(By.linkText("Categories")).click();
driver.findElement(By.linkText("Create a Category")).click();
driver.findElement(By.linkText("Cancel")).click();
driver.findElement(By.linkText("Products")).click();
driver.findElement(By.cssSelector("a.DisplayAdminProductsLink")).click();
driver.findElement(By.linkText("Product1")).click();
There are problems with this. First, it is not give me any By.className() calls. Why? Those first 3 calls will not help me. The framework I am using puts arbitrary things into the name. How can I get it to see the class attribute?
There actually are unique words in the class attribute of all of the above tags. I design my apps so that this is so. Yet it will not use them.
Earlier I asked:
Why is it doing a "driver.findElement().click()"? This is fragile and does not
end up working.
What I need is:
elt = driver.waitFor(By.className("c")); elt.click();
This will work reproducibly.....
I am considering to be removed from the question, as the findElement() code does work. You need to set a general time-out on the driver. It is not very obvious that this can be done, but it can.
So, continuing on....
I can go to the "Options" and change the order of the "Locator Builders" in eclipse. I can put "css" at the top. Then I get:
driver.findElement(By.cssSelector("input[name=\"3.1.1.5.1.1\"]")).clear();
driver.findElement(By.cssSelector("input[name=\"3.1.1.5.1.1\"]")).sendKeys("dan");
driver.findElement(By.cssSelector("input[name=\"3.1.1.5.1.5\"]")).click();
The tags are like:
<input class="form-control LoginUsernameField" ... />
But it does not see the class attribute.... Or I can do this manually.
Selenium looks for a unique identifier to identify elements in a webpage. classNames are a very less desired option for this purpose as they are generally not unique. Ids and names on the other hand are generally unique. This might be the reason why Selenium IDE is not selecting classNames and going for other identifiers.
Selenium IDE records user actions. You would have clicked on the element for Selenium IDE to identify it and that is why you are getting driver.findElement().click().
If you want to wait for element to wait you can try implicit wait.
When you want to use driver.findElement(By.className(str)), are you sure that there is one and only one element in the webpage that is associated with a className? If that is the case you can modify the webdriver code manually to use className.

adding controller later

I'm trying to create an NG app where parts can be enabled/disabled dynamically. The idea is to have an "admin" page, where parts of the app can be enabled or disabled, and then see new functionality appear, in the form of an adjusted menu at the top of the page, and matching routes, controllers, etc loaded into the app (I'm using SocketStream w/ NG).
The first step was to add / remove routes dynamically, for which I found a solution at https://stackoverflow.com/a/13173667 - working well, as far as I can tell.
Next, adding items to the menu bar - easy with ng-repeat on ul/li items.
So the app adjusts its menu and recognizes the corresponding route. So far so good.
The problem comes with registering a controller. I'm calling myApp.controller('SandboxCtrl',[...]) with proper args (same as what worked when initialising statically on startup), but the controller does not appear to get loaded or inited properly. Navigating to the newly added route generates errors such as:
Error: Argument 'SandboxCtrl' is not a function, got undefined
assertArg#http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.1/angular.js:973
assertArgFn#http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.1/angular.js:984
#http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.1/angular.js:4638
update#http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.1/angular.js:14007
$broadcast#http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.1/angular.js:8098
#http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.1/angular.js:7258
wrappedCallback#http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.1/angular.js:6658
wrappedCallback#http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.1/angular.js:6658
#http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.1/angular.js:6695
$eval#http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.1/angular.js:7848
$digest#http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.1/angular.js:7713
$apply#http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.1/angular.js:7934
#http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.1/angular.js:5433
I'm currently at a loss on how to proceed. I've not been able to find a solution on the web. The app is too large to put in a jsFiddle, but I can commit the last changes on GitHub if needed.
Questions: is this feasible? what can I do to debug this? any examples I could look at?
EDIT: The code is now at https://github.com/jcw/housemon (needs node/npm/redis). It's easy to reproduce the problem: launch with "npm start", browse to localhost:3333, go to admin tab, click on "jcw-sandbox" and then "Install". Top menu will update with new a "Sandbox" entry. Clicking on that entry generates the error shown above.
Oh, almost forgot: relevant code is in client/code/app/main.coffee and client/code/modules/routes.coffee ...
The answer turns out to be two-fold:
the NG calls were made from SocketStream RPC callbacks, and had to be wrapped in $scope.$apply calls - my bad, didn't know about this SS/NG interaction
the rest of the solution was outlined by #matys84pl - pick up $controllerProvider (and $filterProvider) early on, so they can be called at a later time instead of the normal "app.controller" and "app.filter" members, which don't seem to work anymore later on
Example code in GitHub, I'll link to a specific commit so this answer stays valid:
https://github.com/jcw/housemon/commit/f199ff70e3000dbf57836f0cbcbb3306c31279de

Agile Toolkit localization

I read the answer here for a question about bilingual atk, but I wonder if the framework is adapted for international use?
I tested the CRUD functionality (v 4.2) and cannot see that e.g. the labels of the buttons (Add, Edit, Delete) are run through the _() function.
Any plans for that? If needed and ATK is the right path, could I assist? If so how?
Here is the most recent localization branch.
https://github.com/atk4/atk4/tree/locale
they call $this->api->_() which you can override and make it call _() or any other way.
$this->add('translation/Controller_Basic')
->setLocale('de') // default language
->setModel('translation/Translation'); // use your model if you wish
$this->routePages('translation'); // adds translation/admin page
$this->add('Button')->setLabel('Localization Test');
If you use the translation controller with debug argument, it will add smileys to all the non-localized strings:
$this->add('translation/Controller_Basic',array('debug'=>true))
Records which has no translation will be automatically added through the model.

Resources