Category Archives: Sitecore

UI Automated Testing with Sitecore

home_-_delta

Automated Unit Testing with Sitecore can be a bit tricky because of the context and configuration that goes along with Sitecore, but from a pure UI perspective, testing can be automated with a screenshot comparison approach. I posted an article over at Code Project called Screenshot Smoke Testing. I came up with this approach for my current client witch is a Sitecore engagement. This approach would actually work with any ASP.NET project, not just Sitecore and it  allows one to catch a number of issues that could surface while writing very little code.

Instead of having to write a test script for every element on the page, the test can simply take screenshots of the site and compare them against approved versions. If there is a difference, than the delta would get highlighted and the test would fail.

Feel free to check out the article and let me know what you think: http://www.codeproject.com/Articles/688936/Screenshot-Smoke-Testing

Filtered Search Sample (part 4 – search service)

  • In Part 1 we talked about interacting and consuming the search service
  • In Part 2 we discussed a Tagging strategy to allow for a Filtered Search to be possible
  • In Part 3 we looked at our service layer which worked with our Ajax calls
    Screen1

Today we are going to dive into our search service and talk about some of the customizations we did to make Lucene and Sitecore do what we wanted from an Indexing and Search standpoint. If you recall from Part 3 (or by looking at the above screenshot) we had a piece of code that would create a new Searcher and call the GetItems method passing in the SearchParams we created based on the Ajax call that was being executed. Now we are going to dive into what the Searcher class is.

Screen2

Our Searcher class is just a modified version of the code that you can get from Sitecore’s Shared Source library and you can watch a webinar to get more detail on it here so I won’t go into a lot of detail. Instead I will just go over some of the modifications I have made. Above you can see that we have an additional call to an ApplyTagFilter method that will help us filter by the Tags that were checked by the user in our UI.

Screen3

The ApplyTagFilter method will iterate through the GUIDs in our Tag collection and add them as an OR criteria to our query. Seeing how that our Tags are GUIDs and therefore unique you will notice we are just querying against the BuiltIn.Content field.

Indexing

Screen4

So the AdvancedDatabaseCrawler is inheriting from the standard Sitecore DatabaseCrawler. One requirement our client had is that they want the ability to hide an item from search fairly easily even if the actual content is available on the site. The way I accomplished this is to simply have a Field on the Base Page Template called “Hide From Search”. Keying off of this field we can then tell Sitecore to not index the item if this field is checked.

Screen5

When setting up the configuration for each index you can specify the boost, but I have taken it a step farther here allowing the Content Managers to overwrite the Boost associated with the Template or Page.

Screen6

So above we are taking the default Boost associated with the document based on the configuration and adjusting it with the Search Boost value if the field exists.

Screen7

If you look at the article above it is tagged as a White Paper. If the user does a search for items that are tagged as an Article / White Papers then this article wouldn’t actually come up in our results because the White Paper GUID is different than its parent. We could dynamically bubble up a Tag’s ancestry at Query time but that would be a little bit of a performance hit so instead we are going to crawl up the Tag hierarchy with the crawler. You can see how we are doing this below.

Screen8

 

Debugging

When you are setting up your custom crawler and doing some tricky things with searches you will inevitably need to figure out why-in-the-heck you are not getting the results you are expecting. The best way to do this is to set a break point at the point where your code is about to call RunQuery and copy the LuceneQuery value. Once you have this query you can use the Luke client or Sitecore Rocks VS.NET Plug-in to run the query and see why it is not pulling back what you are expecting.

Screen9

It’s a Wrap

Well it took four posts, but hopefully you got a good understanding of the approach we used for Filtered Search on a recent project. If you want to learn a bit more, there is a great webcast for the Sitecore Virtual User Group in July that has a great overview and some samples as well. Lucene is quite fast and for future projects I will probably take it a step further where we can leverage Lucene for more than just Search and Filtered Search and use it for presentation elements as well such as menus, lists and summaries.

 

 

Filtered Search Sample (part 3 – service layer)

In Part 1 we talked about interacting and consuming the search service. In Part 2we discussed a Tagging strategy to allow for a Filtered Search to be possible. Today we are going to look at the service layer that is called by the presentation layer.

SearchService.cs

Screen1

Here we have the method signature that is called by the client. So as we went over in Part 1, if the user searches for “cost savings” and selects the Industries of “Construction” and “Energy” the following url is used in the Ajax call on the client end.

htttp://www.domainname.com/Services/SearchService.svc/SearchInsightsJson?&Category2={017E7503-83A1-4A45-9053-4C6755715162}|{66310DCB-3BD1-4C4C-9AFA-FBBC8D4BA967}&page=1&perPage=8&searchString=cost%20savings

This url ends up calling the SearchInsightsJson method within the SearchService web service class and it will pass in the GUIDs associated with Construction and Energy, the current page the user is on, the results the client wants per page the searchString is set to “cost savings”.

You will notice the method has two attributes that mark the method as an OperationContract (available via the web service) and the WebGet attribute is stating that the response should be in a JSON format.

Prevent Client-side Caching

Screen2

The three lines of code above help prevent any caching that may occur by the client. Technically because the url would be unique for each search request we could remove this block of code all together with little impact but this ensures if a new item has been published since the client ran the query that it would get picked up in the search results.

Building-up the Criteria

Screen3

The code shot above shows the approach to calling the Search service. We are populating a SearchParam object with the information needed by the Search Service to run the query.

  • LocationIds = the top folder or folders within the CMS we want to search.
  • TemplateIds = the page types we want included in our search.
  • FullTextQuery = the key word or phrase the user entered in the search form.
  • ShowAllVersions = this specifies if we want to search all versions of a page or just the latest
  • Language = sets the language context of our search (for multi-lingual sites)

We then check each Category parameter and if it is not null we will add the pipe delimited GUIDs to the Tags collection.

Search Call and Setup Paging

Screen4

The first piece is calling our Search Service and passing in our Criteria. We can then set some parameter values that we will include in our JSON result so our presentation can provide paging functionality without having to do the logic on their end.

We set the TotalResults is set to the hits.Count and the CurrentPage is the page that was passed in originally so the client doesn’t have maintain state.

The results we get back from the Search Service is actually just a List of what we are calling a SkinnyItem, that is to say, it is just a pointer to the actual Item along with some other meta data. SkinnyItem has a GetItem() method that will retrieve the full Item from Sitecore but if we have 1000+ items that match our search and we truly only need to fetch the ones that are being displayed on the current page (8 of them), it would be a lot of unnecessary overhead to do this.

We end up creating an s and an e variable that we will use in the next code snippet. e is either going to be (perPage * page) or the total results from the search whichever  is less. s is either ((perPage * page) – perPage) or the hits.Count – perPage whichever  is less (as long as it is >= 0).

Screen5

Above we are iterating through the SkinnyItems between our s and or e count and grabbing the full Item out of Sitecore in order to populate a SearchResultItem and add it to our resultItems collection. This seems like an extra step because we are going from a SkinnyItem to get a Sitecore Item to get a SearchResultItem but there is a logical reason. The SkinnyItem is essentially a representation of the raw search result from Lucene. The Sitecore Item is acting as full data record or Domain Object and the SearchResultItem is a simple Data Transfer Object containing only the fields needed by the view.

We set the JSON resultData.Results equal to the collection of SearchResultItems we just created and we set the TotalPages equal to the total records divided by items per page and round up.

We set the SearchUrl with the value that was passed in and provide it back to the client so it doesn’t have to worry about state.

The PageStart is what the starting page in the page navigation should be. This is hard coded right now with a paging of 5 but this could come from a config file or even the CMS. This allows our client to setup paging of 6 through 10 when a current page of 7 is passed in (see below).

Screen6

Next Up

So that is what our web service looks like. While writing this blog post and taking screenshots I see some areas of improvement and refactoring but you should get the idea of what the service layer should be doing. Part 4 will focus on our actual Search service. This will go into detail about how we customized our Crawler, setup our Indexes and leveraged the advanced searcher / crawler from the Sitecore Shared Source library – if you want to get a heads start on that I recommend this video.

Filtered Search Sample (part 2 – tagging structure)

For the second part of the Filtered Search Sample we are going to look at the data layer to support our Filtered Search presentation. The implementation here is being implemented with Sitecore as the data store but the pattern could easily be some other CMS or just a database.

Tagging

Screen1

Our filtering is based on searching across the repository for items that have the criteria we are looking for. This requires two basic things. The first being our items need to be tagged in some fashion and the second is that those tags are made available to the user to include or exclude from the results. Above you can see that we have a hierarchical tagging structure. This allows nested tags such as an Article and a White Paper can have the same parent and then we can allow the parent of “Article / White Paper” be used in the Filtered Search.

You can also see that a Tag is more than just a string field. This allows us to associate a Tag with an icon and a description if we want. The PowerFilter property of the Tag is what we are using to tell the system if we want the Tag to be listed in the Filtered Search module. We are using Tags for a lot more than just the FilteredSearch so this field allows us to flag the Tag as an item we want the user to be able to filter on.

Configuration

Screen3

Here you see we have a configuration element in Sitecore that allows a content manager to specify the title, the instructions, how many results should be displayed per page and which Tag Categories should be used.

View

Screen2

The PowerFilter Sublayout is just an .ascx User Control that inherits from the SublayoutBase – if you are not familiar with Sitecore it is merely a base class that makes it easier to get access to the context in which the control is being used. Here we are setting the context of control to the PowerFilter item we just discussed. We are binding an ASP.NET Repeater to the Tag Categories chosen and we actually have a repeater within a repeater that will list out each Tag within the Tag Categories if it’s PowerFilter property is set to true.

Next Up

We have covered the presentation and now the tagging structure we setup using Sitecore; next we will go into implementing the service layer that will respond to the Ajax calls that actually get the results to be displayed.