I am currently automating my tests with Specflow and Selenium. I have created simple functions which I would like to re-use with Specflow. The problem I am encountering is, once I have defined a step as When, later on I cannot re-use this step with Then.
So for example:
Scenario: 1
Given user navigates to 'http://www.anyURL.com/'
When user inserts 'data' into field with ID 'id'
And user clicks on button with ID 'id'
Then user sees 'x' amount of search results
So this is scenario 1. Now in scenario 2 I want to use
"When user inserts 'data' into field with ID 'id'"
as:
"Then user inserts 'data' into field with ID 'id'"
But as in my code, my function starts with [When(#"user inserts '(.*)'.... Sepcflow doesn't match this.
I remember that Cucumber used to do this automatically.
Any help much appreciated!
In specflow you just have to add another attribute to the method you want to bind:
[When(#"user inserts '(.*)'"]
[Given(#"user inserts '(.*)'"]
public void UserInertsData(string data)
{
....
}
Related
we have a cleanup hook in our automation framework which tagged from the cucumber feature file
E.g
#cc_task_clean_up_hook_enrol_A
Scenario: Person can enrol_A
When I select the context menu
Then I am able to enroll the patient into 'enrol_A'
the implementation of the hook (#cc_task_clean_up_hook) is
#After(value = "#toc_task_clean_up_hook_enrol_A", order = HookOrder.CLEAN_UP_APP_AFTER)
public void cleanUpTOC() {
this.patientContextPage.selectedContextMenuItem("Pathway");
this.pathWayPage.selectReferences("Enroll in Pathway");
this.pathWayPage.deactivateEnrollment("enrol_A", "Withdrawn");
}
So exactly the same way we need an another scenario like
Scenario: Person can enroll_B
When I select the context menu
Then I am able to enroll the patient into 'enrollB'
So we can implement another hook as follows, the difference is the parameter type "enrollB"
#After(value = "#toc_task_clean_up_hook_enrollB", order = HookOrder.CLEAN_UP_APP_AFTER)
public void cleanUpTOC() {
this.patientContextPage.selectedContextMenuItem("Pathway");
this.pathWayPage.selectReferences("Enroll in Pathway");
this.pathWayPage.deactivateEnrollment("enrol_B", "Withdrawn");
}
So is it possible to consolidate these two methods and write only one generic clean up hook, based on the passed parameter? Your help is much appreciated.
You can add the scenario object to the arguments passed to the after hook. The framework will inject the currently executing scenario to the method.
public void afterMethod(Scenario scenario){}
You can use the getSourceTagNames() method of the Scenario object which will return you the collection of tags for the current executing scenario. From this collection you can determine if you have the tag ending with 'enroll_A' or 'enroll_B'.
Or you can use the getName() method which returns the description of the current scenario. So you will get either 'Person can enroll_A' or 'P..... enroll_B'. Just need to parse again.
You can modify the Then step to pass the enroll type to step definition. Store this in a variable. Use this variable in your after hook. But this will require the after hook to be in the same class.
Also you will need to change the value parameter of After hook to - {"#toc_task_clean_up_hook_enrollA,#toc_task_clean_up_hook_enrollB"}.
One observation that these two seem to have the same steps, if so then have you considered ScenarioOutline instead.
on my edit/add page for SQLFORM.grid I would like to get the values of some fields on process. In case this value in combination with another field (userid) already exists user has to be notified about it.
any suggestions?
You can simply define the validator for one of the fields so it doesn't allow duplicates when the other field is also a duplicate, and then let the standard form validation process handle everything:
db.define_table('mytable',
Field('userid', 'reference auth_user'),
Field('otherfield',
requires=IS_NOT_IN_DB(db(db.mytable.userid == request.vars.userid),
'mytable.otherfield')))
Whenever a form is submitted, the IS_NOT_IN_DB validator will return an error if the value of "otherfield" is duplicated among the set of records where "userid" is also duplicated.
I'm not sure what is the way to do this , so here I ask:
I have a Person model and Event model, and a connection table Person_Event.
The interface that I got now works in the following way:
A person is logging in and his id is being send via URL
The person is selecting events he is interested in from the cGridView (checkbox column)
Writing some comment
4.Pressing send button , and the following create action is being triggered:
public function actionXcreate()
{
$model=new Person_Event;
if(isset($_POST['Person_Event']))
{
foreach ($_POST['selectedIds'] as $eventId)
{
$pmodel=new Person_Event;
$pmodel->person_id=$this->_person->id; //the id of the person who is logged in
$pmodel->attributes=$_POST['Person_Event']; //the comment
$pmodel->event_id = $eventId; //all the events he checked in the grid
if (!$pmodel->save()) print_r($pmodel->errors);
}
$this->redirect(array('site/success'));
}
So far , all is logical and simple. However , what I end up is that the comment the person wrote is being duplicated to every person_event row in the DB.
I want to put a text box in each row of the grid , and the commnet that will be written there will go to the specific event.
Now , I found this topic in yii about "admin-panel"
which is kind of helpful , BUT:
I already have a foreach in the action , the one that matches the person's id with the event's id , so how can I put another individual comment for each combo?
The default CGridView supports only basic functionality, you would need to extend CGridView or use an extension to make columns editable
Easiest way to do this is use something like TbEditableColumn from Yii-booster library
see http://yiibooster.clevertech.biz/extendedGridView#gridcolumns EditableColumn in the additional column types section
If you do not like or wish to use twitter-bootstrap styling a standalone extension like http://www.yiiframework.com/extension/eeditable will help.
Alternatively you can extend CGridView yourself to extend it to support column level editing
Is it possible to do an overload of the actions in the controller? I haven't found any info about it and when I tried, I got this error:
The current request for action 'Create' on controller type 'InterviewController' is >ambiguous between the following action methods:
System.Web.Mvc.ViewResult Create() on type >MvcApplication4.MvcApplication4.InterviewController
System.Web.Mvc.ViewResult Create(Int32) on type >MvcApplication4.MvcApplication4.InterviewController
I've tried to do this on another way and I also get a new error that I can't fix. In fact, I created a new action (called create_client instead of create)
I need 2 ways of creating an "opportunite".
I just call the action, and I receive an empty formular in which I just have to insert data.
From a client's page, I must create an "opportunite" with the client that's already completed when the form is displayed to the user. (there is a need of productivity, the user must perform actions as fast as possible).
In the table "opportunite", I've got a column called "FK_opp_client", which is equal to the column "idClient" from the client's table.
I don't get how I can do the second way.
I've created a new action in the controller.
'
' GET: /Opportunite/Create_client
Function Create_client(idclient) As ViewResult
'Dim FK_Client = (From e In db.client
'Where(e.idClient = idclient)
' Select e.nomCompteClient).ToString()
'ViewBag.FK_client = New SelectList(db.client, "idClient", "nomCompteClient", idclient)
Dim opportunite As opportunite = db.opportunite.Single(Function(o) o.idOpportunite = 5)
opportunite.FK_Client = idclient
ViewBag.FK_Client = New SelectList(db.client, "idClient", "nomCompteClient", opportunite.FK_Client)
Return View(opportunite)
End Function
I've tried a few things to get what I wanted, the last one was to copy what was done in the "Edit" action, but for an empty rank. (so I created an empty rank in my DB). I don't think it was a good idea (imagine someone wants to update the DB where idOpportunite = 5...)
Any better ideas?
If you want to keep those two methods under the same name, you will have to implement an ActionSelectionAttribute to decorate them, or use them with different verbs (for example POST and PUT). Please read more details on action method selection process here (old but still true).
Different approach might be making your parameter optional and make action to check if it has been passed or not (through nullable type).
I have a big, flat table:
id
product_id
attribute1
attribute2
attribute3
attribute4
Here is how I want users to get to products:
See a list of unique values for attribute1.
Clicking one of those gets you a list of unique values for attribute2.
Clicking one of those gets you a list of unique values for attribute3.
Clicking one of those gets you a list of unique values for attribute4.
Clicking one of those shows you the relevant products.
I have been coding Rails for about 4 years now. I just can't unthink my current approach to this problem.
I have major writer's block. Seems like such an easy problem. But I either code it with 4 different "step" methods in my controller, or I try to write one "search" method that attempts to divine the last level you selected, and all the previous values that you selected.
Both are major YUCK and I keep deleting my work.
What is the most elegant way to do this?
Here is a solution that may be an option. Just off the top of my head and not tested (so there is probably a bit more elegant solution). You could use chained scopes in your model:
class Product < ActiveRecord::Base
scope :with_capacity, lambda { |*args| args.first.nil? ? nil : where(:capacity=>args.first) }
scope :with_weight, lambda { |*args| args.first.nil? ? nil : where(:weight=>args.first) }
scope :with_color, lambda { |*args| args.first.nil? ? nil : where(:color=>args.first) }
scope :with_manufacturer, lambda { |*args| args.first.nil? ? nil : where(:manufacturer=>args.first) }
self.available_attributes(products,attribute)
products.collect{|product| product.send(attribute)}.uniq
end
end
The code above will give you a scope for each attribute. If you pass a parameter to the scope, then it will give you the products with that attribute value. If the argument is nil, then the scope will return the full set (I think ;-). You could keep track of the attributes they are drilling down in in the session with 2 variables (page_attribute and page_attribute_value) in your controller. Then you call the entire chain to get your list of products (if you want to use them on the page). Next you can get the attribute values by passing in the set of products and the attribute name to Product.available_attributes. Note that this method (Product.available_attributes) is a total hack and would be inefficient for a large set of data, so you may want to make this another scope and use :select=>"DISTINCT(your_attribute)" or something more database efficient instead of iterating thru the full set of products as I did in the hack method.
class ProductsController < ApplicationController
def show
session[params[:page_attribute].to_sym] = params[:page_attribute_value]
#products = Product.all.with_capacity(session[:capacity]).with_weight(session[:weight]).with_color(session[:color]).with_manufacturer(session[:manufacturer])
#attr_values = Product.available_attributes(#products,params[:page_attribute])
end
end
Again, I want to warn you that I did not test this code, so its totally possible that some of the syntax is incorrect, but hopefully this will give you a starting point. Holla if you have any questions about my (psuedo) code.