Simple algorithm to make wizard navigation less linear - c#

This should be a simple logic problem but for some reason I've been struggling with it for hours trying to come up with a semi-clean algorithm to implement this. I'm using MVC3 with a SQL Server background, but even if you don't know about MVC you might still be able to help me with the algorithm.
I'm writing an application that makes use of a wizard-like interface. For now, the navigation between those wizard screens is very linear (the next button goes to the page immediately after, previous button goes to page immediately before). Due to scope change (fun, I know) I've now been told to make this less linear.
For the first run-through, the user has to visit all pages in linear order, like so:
Step 1
Step 2
Step 3
SubStep 1
Sub-SubStep 1
Sub-SubStep 2
SubStep 2
Sub-SubStep 1
Sub-SubStep 2
...
SubStep *n*
Sub-SubStep 1
Sub-SubStep 2
Submission
Where n is variable based on something that was entered in Step 2.
After the wizard is submitted it is reviewed by an administrator. If they find information missing they can unlock certain pages. When the user goes back to enter that information they should only be able to view those certain pages. For example, navigation might be like so:
Step 2
Step 3
SubStep 1
Sub-SubStep2
Submission
My current implementation consists of a table in the database that keeps track of the unlocked pages. When the "Next" button is clicked it calls a method to determine what the next page is. Because of the strange and variable navigation that takes place in Step 3 this method is an if-else branching nightmare which is easily broken.
Any suggestions on simplifying this would be greatly appreciated.

If you create a tree structure that represents the navigation hierarchy, a preorder traversal of the tree will hit the pages in the desired linear order. You can run such a traversal, and when you hit the current page, you can continue the traversal until you find an unlocked page, which will be the desired next page.
Pseudocode:
class TreeNode:
string name
List<TreeNode> children
string findNextPage(TreeNode node, Set<string> unlockedPageNames,
string currentPageName, ref bool currentPageFound):
if currentPageFound && unlockedPageNames.Contains(node.name):
return node.name
if node.name == currentPageName:
currentPageFound = true
foreach child in children:
result = findNextPage(child, unlockedPageNames,
currentPageName, currentPageFound)
if result != null:
return result
return null
string findNextPage(TreeNode node, Set<string> unlockedPageNames,
string currentPageName):
bool currentPageFound = false
return findNextPage(node, unlockedPageNames,
currentPageName, currentPageFound)
Note that you need a root node, whose children must be Step 1, Step 2, and Step 3. Pass this root node to the last findNextPage() function.

Related

Running feature file multiple times without passing a value from feature file

I have a feature file like this.
Feature: Verify captured document values against CMS for
#test
Scenario: Verify user can open and view document
Given user is in QueueHandling home page Dashboard
And user has batches assigned to himself/herself
When user selects a batch from batch list
Then selected batch is opened in a new window
And user views all identified documents
#test
Scenario Outline: Verify data in document
When user selects "<documentType>" document and compare data
Examples:
| documentType |
| Settlement Coversheet|
| AssetLoanAgreement |
#test
Scenario: Verify user navigates back to QueueHandling home page Dashboard
Given user navigates back to QueueHandling home page Dashboard
I want to run it multiple times within the code.
"When user selects a batch from batch list" in this step I want to pass batch number one by one each time this feature is running.
I tried to add a loop to BeforeFeature like this.
[BeforeFeature]
[Obsolete]
public static void BeforeFeature()
{
while (queueNumberCurrentIndex < 2)
{
string featureAddOnText = queueNumberList[queueNumberCurrentIndex];
feature = extent.CreateTest(FeatureContext.Current.FeatureInfo.Title + " \"" + featureAddOnText + "\"", FeatureContext.Current.FeatureInfo.Description);
Hooks.testlog = extentLog.CreateTest(Properties.getProperty("projectname"));
feature.AssignCategory("Regression");
}
}
But this keeps on running forever. What should I do?
Cucumber is not an environment to program in.
If you need to program, you need to either push the programming down into your step definitions or better yet helper methods called by your step definitions, or pull the code up, so you do things like have scripts that dynamically create features.
Most of the time pushing the programming down is the simplest answer. If you implement a step definition as a single call, you end up in a pretty powerful programming environment in your programming language of choice. Here you can loop, collate, connect to external resources etc. etc.
In you example the programming is all focused on how something is being done. The first step to pushing this down is to understand WHAT your scenario is trying to do and WHY that is important. Currently your scenario fails to do this. It claims that WHAT it is about is that a user can open and view a document. If this was actually the case your scenario would be
Given the user has a document
When they open their document
Then they should see their document
But your scenario is all about batches verification settlement, loans. You won't make any progress without expressing a much clearer description of WHAT the combined effects of all these interactions are for, and WHY this particular combination of interactions is important.

Find all elements by xpath attribute (aria-required = true) .//*[#aria-required='true']

I'm trying to get a list of IwebElements that contain the attribute aria-required. Reason why I'm trying to get this list, is so that i can check if all required fields are necessary for the user to fill out, before he can continue to the next page.
So far after 2 days of searching I'm still convinced that it shouldn't be that hard. I'm using the expression:
".//*[#aria-required='true']"
From my research that would mean that it will search for ALL the elements starting from the root of my webdriver.
[TestMethod]
public void CreateProjectWithoutRequiredFields()
{
GoToProjectPage();
tracking = CM.GoToNewlyCreatedFrameAfterClickButton("ftbNew", tracking, theDriver);
CM.Wait(2000);
bool succesSave = false;
CM.LogToFile("Create project whitout required fields", tracking);
foreach (IWebElement e in theDriver.FindElements(By.XPath(".//*[#aria-required='true']")))
{
FillDataInNewProjectPage();
e.Clear();
CM.GetDynamicElementById("btnSave", theDriver).Click();
try
{
CM.GetDynamicElementById("titel", theDriver).Click();
}
catch (Exception)
{
succesSave = true;
NUnit.Framework.Assert.IsFalse(succesSave, "The page is saved with succes, without entering text in the following required fields : " + e.GetAttribute("id").ToString());
}
CM.Wait(1000);
}
}
I will try to explain what i did here:
First i went to a overview page with all my existing projects. On this page i clicked the ftbNew button to create a new project. The driver is automatically switch to the correct frame (i now the right frame is selected because i used this line on other page's.)
then the line
foreach (IWebElement e in theDriver.FindElements(By.XPath(".//*[#aria-required='true']")))
{
should normaly find a the elements in my driver with an attribute "aria-required='true'"
Then it would fill in the page with data, clear the first element that is found from its data en try to save it.
if the element titel is found on the page, than we are still on the same page en the save action wasn't successful ( <- so this is good)
so next we again overwrite every field on the page and clear this time the second element that is found.
en so on...
What I'm guessing, that xpath has difficulty finding the 'old' aria-required attribute... When i try to validate my expression using firebug and other xpath checkers, the aria-required attribute isn't always present. Sometimes it finds the input field sometimes it doesn't.
source code page
As you can see in the firebug console not all attributes are loaded, account-manager has a aria-required attribute, but project leader doesn't. If i inspect the element again, but this time click project leader. The attribute will be loaded. Very strange....
Extra info: I am using frame's and i know a lot can go wrong if you are situated in the wrong frame, but i am sure that he is looking in the correct frame. Especially because i can't find the elements using firebug with the above expression. If i change my expression to .//input, it will find the elements but also selects input fields that aren't required.
In advance i want to thank everybody that want to look into my problem :)
Adriaan
Based on your description of the behavior on the page, you cannot rely on the aria-required attribute to indicate a required field. I think that you should go back to the developers of the site to ask them to give you a reliable handle. It may be as simple as looking for the "*" as the last character of the label associated with the input field, but that's kind of annoying to have to deal with.
As an aside, you should consider catching a more specific exception for your success case. Right now, if ANY exception happens, you'll declare success, but that may not be what you really want to do.

Passing data to a different controller/model

I am very new to MVC in general, so this might sound very silly. However, I don't quite get how to connect my data in the following scenario:
I am creating a project where I have basically two tables
Table 1
pid | pname | etc
Table 2
id | pid | etc
Now, I have successfully implemented a controller where I populate a list with the data in table 1 using "date range" as a parameter.
URL 1
/table1object?search=Search&endDate=2015-02-20&fromDate=2015-02-20
As the tables shown, each row in this view is connected to a certain row in table 2. So, for this I am generating a link which basically sends me to the details of the second controller by looping through the current model.
#Html.ActionLink("Details", "Details/" + #result.pId, "table2Object", null)
Now, my controller number 2 (Details) is only accepting an id to display that specific information of the Table 2.
URL 2
/table2object/Details/96
The Question
Since I am only passing this id to access the second controller, I am going to be able to see only one item at a time. What if I want to add a previous and next buttons to navigate through items in the second controller, but in the range established in the first controller? How can I do this?
Thank you
A first way to do it without respecting any best practices is to save your search result (Controller1) inside a Session variable.
Session["CurrentResult"] = YourViewModelInController1 ;
and then in Controller 2
ActionResult Details(int id)
{
var prevViewModel = Session["CurrentResult"] ;
// Do what you want to find the prev and the next from prevViewModel
}
Of course you can't get the next and the prev if you access your DetailController without displaying table1.
And also, there will be many side effects. e.g : when user make 2 search at once, ..
The second good and correct way to do it, is to re-execute a search to find the previous and the next on Controller 2 Details. Independently of the previous
ActionResult Details(int id)
{
// Do a real search to find the next and the previous
}
Considering you have the idea how to pass values and work in a controller, i can give you a suggestion as below:
You can load your items in a list within a given date range first. Pass that list to 2nd controller And then traverse that list by clicking prev and next using a counter.
Hope this helps,
Thanks

Umbraco 4.7 - nodes created at root level of site are saved to umbracoNode table with level = 2

Here's a simplified version of my Content tree.
- Content
- Articles
- Article 1
- About Us
Let's assume I add 2 nodes, one as a child of Articles and the other at the root level. My tree would now look like this.
- Content
- Articles
- Article 1
- Article 2
- About Us
- Terms
Both Article 2 and Terms are saved to the umbracoNode table with "level" set to 2. This is not the correct level for Terms; it's level should be 1.
This is causing publishing to fail, specifically at umbraco.cms.presentation.editContent.Publish:
if (_document.Level == 1
|| new cms.businesslogic.web.Document(_document.Parent.Id).Published)
{
// Code that publishes the node
}
What's happening is that since level DOES NOT equal 1, it evaluates the 2nd part of the conditional. Once the Document is instantiated with the current node's parent id (-1), it checks for cmsContentVersion entries with that id. However, it's the root node, and nothing exists in cmsContentVersion for the root, so it goes kaboom.
A few things that may or may not be of interest:
We are in the process of upgrading from v3.6 -> v4.7
Right-Clicking the node and publishing works
The link to the document after right-clicking and publishing is simply #, which is also incorrect. I'm unsure if this is related or not.
Any help would be greatly appreciated.
The fix actually ended up being pretty simple, hope this saves someone a headache.
I simply had to update level to 0 for the root note (it was 1 prior to udpating it).
UPDATE umbracoNode
SET [level] = 0
WHERE id = -1
I assume this was some artifact of version 3 that, for some reason, wasn't addressed earlier in the upgrade.

Data Pagination: SQL Server 2005 + SL3

Hey everybody, got an interesting question I think. I've got a Silverlight3 application which is querying a SQL Server 2005 database asynchronously. However, some of the data sets the queries return are massive and so I'm looking into data pagination.
Goals:
1) Just in time data calls - I only want to query for page 3's data when the user clicks to go to page 3.
2) I want a slider to control which page I'm on - SliderControl is SL3 with its movement binded to a stored procedure call (is my initial guess as to an approach).
3) Read ahead data as a label for the slider. So the slider will say "Page 1 of 50" or perhaps "Gant - Hart". Some sort of indication as to where you are in the data without actually querying for all the data until the user drops the slider into position.
I'm almost positive that I need to do some sort of a query to get the total number of rows and some sort of bookmark data that the query will eventually return. Otherwise I don't know how to split up the slider nor would I be able to do page labels (The gant to hart stuff).
Anyone have experience with this kind of thing? Thanks.
There are usually at least 2 separate lookups involved here, the first to get the pager information and a second to retrieve the data appropriate to the current page.
The pager (slider) needs to know the total number results that would be returned if you weren't doing paging. This is needed to show the pretty "Total # of Results" indicator for your use and to calculate how many total pages you've got. In your case you're going to want to format a nice visual display of the pages for selection (determined by your desired records/page value) in your slider. That's your first query; it should return just a single scalar int or long and you can calculate what you need for your slider display.
The second results include the actual records to be displayed for a given page.
Step 1 of 2 for a page of data is to run a query to filter and order your results by a primary key present within the data you're returning, throwing the key values in a temp table with an AUTO_INCREMENT/IDENTITY field or getting the row number over for the results of a derived table. (Summary: 2 fields, 1 for sequence/1 for primary key to join in step 2).
Step 2 of 2 is to join the key values with your tables which contain data, ordering by the sequence determined in Step 1, selecting only rows numbered (desired start row) to (desired end row) as determined by the page number and page size you've selected.
A stored procedure is strongly advised to keep the data on the server during this operation while you're having to examine more data than you really care to bring back to optimize bandwidth.

Categories

Resources