Handle WebDriver browser prompt for ADFS credentials Selenium C#

While working on Automating the UI using Selenium C#, you might encounter a scenario where a website requires to be authenticated with ADFS. The browser would prompt the user to provide the AD domain credentials to authorize for accessing the website. So automating this scenario with the Selenium C# API, would require you to provide the domain credentials programatically.

The WebDriver used is Firefox with Geckodriver. Another post explains the working of the automation and initialization of the WebDriver in detail here.

Code sample is as shown below:

public static IWebDriver driverWeb;
driverWeb.Navigate().GoToUrl(link);
try
{
	driverWeb.SwitchTo().Alert().SendKeys("Username" + Keys.Tab + "Pwd");
	driverWeb.SwitchTo().Alert().Accept();
}
catch { }

try
{
	driverWeb.FindElement(By.Id("acknowledgeCookieId")).Click();
}
catch { }

Take screen-shot of webpage with Firefox and Geckodriver using Selenium with C#

Gecko is a web browser engine used in many applications developed by Mozilla Foundation and the Mozilla Corporation. It is a proxy for using W3C WebDriver-compatible clients to interact with Gecko-based browsers, in this case, Firefox. Gecko Driver acts as the bridge between your script in Selenium and the Firefox browser.

Firefox browser implements the WebDriver protocol using an executable called GeckoDriver.exe. This executable starts a server on your system through which all interaction happens. It translates calls into the Marionette automation protocol by acting as a proxy between the local and remote ends.

To generate the screenshot, the web page will be loaded in the Firefox WebDriver and the height of the window adjusted appropriately. Every GeckoDriver process spawns 4 Firefox processes which you can check through Task Manager. The Preview generation logic can be written as a Web Service and hosted on IIS. The AppPool would require to be run with LocalSystem permission to enable communication between GeckoDriver and Firefox WebDriver.

The below code will run Firefox WebDriver in headless mode:

public static IWebDriver driverWeb;
public static FirefoxOptions optionsFirefox;
static int totalWidth = 1024;
static int additionalHeight = 220;
static int additionalHeightOffset = 2180;
static int normalHeight = 768;
static int explicitWaitTime = 5;

Setup browser profile:

static Firefox()
{
	try
	{
		optionsFirefox = new FirefoxOptions();
		FirefoxProfile profile = new FirefoxProfile();
		profile.SetPreference("permissions.default.desktop-notification", 1);
		profile.AcceptUntrustedCertificates = true; //Accept SSL certificates which have expired
		profile.AssumeUntrustedCertificateIssuer = false; //Firefox assumes untrusted SSL certificates are coming from untrusted issuer 
		profile.SetPreference("general.useragent.override", Convert.ToString(ConfigurationManager.AppSettings["driverUserAgent"]));
		profile.SetPreference("layout.css.devPixelsPerPx", "0.9");
		optionsFirefox.Profile = profile;
		optionsFirefox.AddArgument("-headless");
		KillExistingProcesses();
		initializeWebDriver();
	}
	catch (Exception e)
	{
		throw e;
	}
}

Initialize Web Driver:

public static void initializeWebDriver()
{
	try
	{
		FirefoxDriverService service = FirefoxDriverService.CreateDefaultService(Convert.ToString(ConfigurationManager.AppSettings["driverPath"]));
		service.HideCommandPromptWindow = true;
		driverWeb = null;
		driverWeb = new FirefoxDriver(service, optionsFirefox, new TimeSpan(0, 0, Convert.ToInt16(ConfigurationManager.AppSettings["setDriverTimeout"])));
		driverWeb.Manage().Timeouts().ImplicitWait = new TimeSpan(0, 0, Convert.ToInt16(ConfigurationManager.AppSettings["setPageLoadTimeout"]));
		driverWeb.Manage().Timeouts().PageLoad = new TimeSpan(0, 0, Convert.ToInt16(ConfigurationManager.AppSettings["setPageLoadTimeout"]));
	}
	catch
	{
		//log exception.
	}
}

Generate Preview:

//This method will be called from a Rest Service and returns a byte array for image.
public byte[] getImage()
{
	byte[] imageDataWeb;
	try
	{
		if (driverWeb == null)
		{
			lock (padlockWeb)
			{
				//double lock check for thread-safety.
				if (driverWeb == null)
				{
					initializeWebDriver();
				}
			}
		}

		if (getHttpResponseCode(link))
		{
			lock (driverWeb)
			{
				driverWeb.Navigate().GoToUrl(link);
				Thread.Sleep(100);
				/*Start Area: Get rendered page height/width and adjust browser accordingly*/
				try
				{
					var javaScriptExecutor = driverWeb as IJavaScriptExecutor;
					var waitPage = new WebDriverWait(driverWeb, TimeSpan.FromSeconds(explicitWaitTime));
					bool readyCondition(IWebDriver webDriver) => (bool)javaScriptExecutor.ExecuteScript("return document.readyState == 'complete'");
					waitPage.Until(readyCondition);

					string HeightScript = "return document.body.scrollHeight";
					long totalHeight1 = (long)javaScriptExecutor.ExecuteScript(HeightScript);
					int totalHeight = (int)totalHeight1;
					if (totalHeight == 0)
					{
						totalHeight = normalHeight + additionalHeight;
					}
					else
					{
						if (totalHeight <= additionalHeightOffset)
						{
							totalHeight += additionalHeight;
						}
					}
					driverWeb.Manage().Window.Size = new Size(totalWidth, totalHeight);
				}
				catch (Exception e)
				{
					_txtSource.TraceEvent(TraceEventType.Error, 0, e.Message);
					_txtSource.TraceEvent(TraceEventType.Error, 0, e.StackTrace);
					_txtSource.Flush();

					driverWeb.Manage().Window.Size = new Size(totalWidth, normalHeight + additionalHeight);
				}
				/*End Area: Get rendered page height/width and adjust browser accordingly*/

				screenshot = ((ITakesScreenshot)driverWeb).GetScreenshot().AsByteArray;

				if (screenshot.Length == 0)
				{
					throw new Exception("Website not responding.");
				}

				driverWeb.Navigate().GoToUrl("about:blank");
				driverWeb.Manage().Window.Size = new Size(totalWidth, normalHeight);
				return screenshot;
			}
		}
		else
		{
			imageDataWeb = returnErrorImage(); //return error image.
		}
		return imageDataWeb;

	}
	catch (Exception e)
	{
		//log exception
		imageDataWeb = doExtractEmptyPage();
		return imageDataWeb;
	}
	finally
	{
		imageDataWeb = null;
	}
}

Standard deviation using C# and Ruby

Standard Deviation definition states:

A quantity expressing by how much the members of a group differ from the mean value for the group.

The logic below calculates the Standard Deviation for the population of values. If the data is being considered a population on its own, we divide by the number of data points, say N. If the data is a sample from a larger population, we divide by one fewer than the number of data points in the sample, n-1. So you can change the formula accordingly.

Ruby code sample:

#sample = Array[1,2,3,4,5,6,7,8]
sample = Array[1,2,3,4,5]

def calc_standard_deviation(values)
    avg = values.sum {|x| x.to_f} / values.size.to_f
    Math.sqrt(values.sum {|x| (x.to_f - avg.to_f) ** 2} / values.size)
end

puts "Standard Deviation is: " + calc_standard_deviation(sample).to_s

Run the Ruby code to test as below in VS Code:

PS C:\code\Ruby test> ruby .\stdDev.rb
#Ruby Output: Standard Deviation is: 1.4142135623730951

C# code sample:

static void Main(string[] args)
{
	//double[] sample = new double[8] { 1, 2, 3, 4, 5, 6, 7, 8 };
	double[] sample = new double[5] { 1, 2, 3, 4, 5 };

	Console.WriteLine("Standard Deviation is: " + StdDev(sample));
}

public static double StdDev(IEnumerable<double> values)
{
	// Get the mean.
	double mean = values.Sum() / values.Count();

	// Get the sum of the squares of the differences
	// between the values and the mean.
	var squares_query =
					from double value in values
					select (value - mean) * (value - mean);
	double sum_of_squares = squares_query.Sum();

	return Math.Sqrt(sum_of_squares / values.Count());
}

C# Output: Standard Deviation is: 1.4142135623731

Update json value using Newtonsoft library C#

We recently had a scenario where we needed to update the empty json array key questions with items having certain json key-value pairs containing user information.

Sample below shows part of the json to be updated:

"screens": [
                {
                  "uid": "<guid>",
                  "body": {
                    "text": "...",
                    "questions": []
                  }
			  }
		  ]

After update:

"screens": [
                {
                  "uid": "<guid>",
                  "body": {
                    "text": "...",
                    "questions": [
                      {
                        "uid": "another guid",
                        "name": "1st name",
                        .... other items
                      },
                      {
                        "uid": "another guid",
                        "name": "2nd name",
                        .... other items
                      }
                    ]
                  }
		}
	]

Below is the sample code that first removes the empty questions array and then adds it back after filling in the User information:

foreach (JToken token in jScreenItem["body"].Children())
{
	JProperty p = token as JProperty;
	if (p != null && p.Name.Contains("questions"))
	{
		token.Remove();
		break; //breaking when empty questions array is removed.
	}
}
foreach (JToken token in jScreenItem["body"].Children())
{
	JProperty p = token as JProperty;
	if (p != null && p.Name.Contains("text"))
	{
		JProperty jquestions = new JProperty("questions", JArray.FromObject(jUsers));
		token.AddAfterSelf(jquestions);
		break; //breaking when filled questions array is added.
	}
}

jUsers above is an array of Objects containing user information.

Create Virtual directory in Visual Studio 2017 IIS Express

You may be facing a scenario where the Images folder of your Project is hosted in another directory on your machine e.g. C:\Data\Images and not under the Project directory structure. In this case, if you need to check whether the Images are loading correctly, you’ll need to create a Virtual directory in your Web application/MVC/WebAPI Project.

Follow the below steps:

1. Right-click on your Project properties and go to the Web tab and provide the URL for the required Virtual directory and click on the button Create Virtual directory. This option edits the ApplicationHost.config file.

2. Run the application and right-click on the IIS Express icon in the status-bar notification tray. Select your website and open the config file.

3. Look for the site tag and find your website and you’ll find the Virtual directory nested tag. Edit the path and physicalPath attribute to the folder on your machine as shown below:

<site name="MyApi" id="2">                 
<application path="/" applicationPool="Clr4IntegratedAppPool">
<virtualDirectory path="/" physicalPath="C:\MyApi" />                 
</application>                 
<application path="/uploads" applicationPool="Clr4IntegratedAppPool">                     
<virtualDirectory path="/" physicalPath="C:\Data\Images\uploads" />                 
</application>                 
<bindings>                     
<binding protocol="http" bindingInformation="*:62216:localhost" />                 
</bindings>             
</site>

After making these changes, refresh the app to view the images in your app via localhost.

To know how to create a Virtual Directory in IIS for a website, check this post.

Show some love for the pit in my PayPal account.

Take care of your health. Click here to know more.

Implement Repository pattern with Entity Framework and C#

In the WebAPIConfig.cs file, register the repository with the Unity Container. This code file defines the delegate where you should put your Web API configuration code. Install the Unity Nuget package in your Project.

var container = new Unity.UnityContainer();

// this will register all Repositories that implement Repository<>. 
// because of the generics, you have to use the concrete type not the interface for injection
container.RegisterType(typeof(IRepository<>), typeof(Repository<>), new HierarchicalLifetimeManager());

Define the Repository interface:

public interface IRepository
{
bool Exists(int id);
IQueryable GetAll();
TEntity Get(int id);
}

Create the Abstract class for the Repository. The LogManager is for the log4net implementation for logging.

public abstract class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
	protected Models.DBEntities db = new Models.DBEntities();

	private DbContextTransaction Transaction { get; set; }

	protected static ILog log;


	public abstract TEntity Get(int id);
	public abstract IQueryable<TEntity> GetAll();

	public Repository<TEntity> BeginTransaction()
	{
		Transaction = db.Database.BeginTransaction();
		return this;
	}

	public abstract bool Exists(int id);


	// select specific columns into a DTO using specified selector
	//

	public abstract TResult Get<TResult>(int id, Expression<Func<TEntity, TResult>> selector);

	public abstract List<TResult> GetAll<TResult>(Expression<Func<TEntity, TResult>> selector);

	public Repository()
	{
		// setup the logger from the instance so we can get the derived type
		log = LogManager.GetLogger(this.GetType()); // should log as the derived type

		db.Configuration.LazyLoadingEnabled = false;

		setDBLogging();
	}

	public void logdb(string msg)
	{
		if (ConfigurationManager.AppSettings["LogDB"] == "true")
		{
			try
			{
				log.Debug(msg.Replace("\r\n", string.Empty).Replace("\n", string.Empty).Replace("\r", string.Empty));
			}
			catch (Exception ex)
			{
				log.Error("failed logging DB activity", ex);
			}
		}
	}

	public void setDBLogging()
	{
		if (ConfigurationManager.AppSettings["LogDB"] == "true")
		{
			try
			{
				db.Database.Log = msg => logdb(msg);
			}
			catch (Exception ex)
			{
				log.Error("failed setting up DB logging", ex);
			}
		}
	}

}

Example Users Respository with a transaction:

using DBMODELS = ApiPersistence.Models;
public class UsersRepository : Repository<DBMODELS.user>
{
	public override bool Exists(int id)
	{
		throw new System.NotImplementedException();
	}

	public override DBMODELS.user Get(int id)
	{
		try
		{
			IQueryable<DBMODELS.user> query = from user in db.users
											  where user.id == id
											  select user;
			var userObj = query.FirstOrDefault();


			return userObj;
		}
		catch (Exception ex)
		{
			throw ex;
		}
	}

	public override IQueryable<DBMODELS.user> GetAll()
	{
		throw new NotImplementedException();
	}
	
	public UserDTO CreateUser(){
		//Example transaction:
		using (var transaction = db.Database.BeginTransaction())
		{
			try
			{
				///code for creating user goes here...
				db.SaveChanges();

				transaction.Commit();
			}
			catch (Exception ex)
			{
				transaction.Rollback();
				log.Error("caught exception", ex);
				throw new RepositoryException("CreateMethod()", "MyRepository CreateMethod() failed", ex);
			}
		}
	}
}

Select query for many-many relationship using linq C#

Select the data between tables with many-many relationship using linq query with Entity Framework and C# as shown in the code below:

db.roles.Where(x => x.resource_id == moduleid && x.resource_type == "MyModule" && x.name == "member").SelectMany(x => x.users.Select(u=>u.id)).First();

User and Role entities in the above example have many-many relationship using a join table in the database. The above example selects the User Id with member role.

Add remove data many-many relationship Entity Framework C#

Consider the example of a Users of an application having many roles like member, admin etc. The relationship in this case is many-many. The two entities in this case are Users and Roles. The many-many relationship is handled by a join table in the database say users_roles. If you import this join table in Entity Framework, it is automatically converted into a many-many association where the Users entity holds ICollection property of Roles entity and vice-versa.

public class User{
    public int id {get; set;}
    public string firstname {get; set;}
    public string lastname {get; set;}
    public ICollection<Role> roles {get; set;}
}

public class Role{
    public int id {get; set;}
    public string name {get; set;}
    public string resource_type { get; set; }
    public Nullable<int> resource_id { get; set; }
    public System.DateTime created_at { get; set; }
    public System.DateTime updated_at { get; set; }
    public ICollection<User> users {get; set;}
}

In the below example, the DBContext object as created as db. Create the Users model object and add roles for the User. This will automatically make an entry in the join table.

//Find existing user
var user = db.users.Find(userid);

//Remove existing role
DBMODELS.role role = db.roles.Include("users").Where(x => x.resource_id == moduleid && x.resource_type == "MyModule" && x.name == "admin").FirstOrDefault();
db.roles.Remove(role);
db.SaveChanges();

//Add new role.
DBMODELS.role newrole = new DBMODELS.role();
newrole.name = "super_admin";
newrole.resource_id = moduleid;
newrole.resource_type = "MyModule";
newrole.created_at = DateTime.UtcNow;
newrole.updated_at = DateTime.UtcNow;
db.roles.Add(newrole);
db.SaveChanges();

user.roles.Add(newrole);
db.SaveChanges();

Eager loading is achieved using the Include() method above.

Resize image preserving aspect ratio in C#

The aspect ratio of an image describes the proportional relationship between its width and its height. For an x:y aspect ratio, if the width is divided into x units of equal length and the height is measured using this same length unit, the height will be measured to be y units.

The below method saves images with different encoder formats of jpeg, png and gif images. You can modify the code for other available formats in C#.

public class ImageUploader
    {

public float Save(Bitmap image, int maxWidth, int maxHeight, int quality, string fileName, string ext)
        {
            // Get the image's original width and height
            int originalWidth = image.Width;
            int originalHeight = image.Height;

            // To preserve the aspect ratio
            float ratioX = (float)maxWidth / (float)originalWidth;
            float ratioY = (float)maxHeight / (float)originalHeight;
            float ratio = Math.Min(ratioX, ratioY);

            float sourceRatio = (float)originalWidth / originalHeight;

            // New width and height based on aspect ratio
            int newWidth = (int)(originalWidth * ratio);
            int newHeight = (int)(originalHeight * ratio);

            // Convert other formats (including CMYK) to RGB.
            Bitmap newImage = new Bitmap(newWidth, newHeight, PixelFormat.Format24bppRgb);

            // Draws the image in the specified size with quality mode set to HighQuality
            using (Graphics graphics = Graphics.FromImage(newImage))
            {
                graphics.CompositingQuality = CompositingQuality.HighQuality;
                graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                graphics.SmoothingMode = SmoothingMode.HighQuality;
                graphics.DrawImage(image, 0, 0, newWidth, newHeight);
            }

            ImageCodecInfo imageCodecInfo = null;
            // Get an ImageCodecInfo object that represents the JPEG codec.
            if (ext == ".jpg" || ext == ".jpeg")
            {
                imageCodecInfo = this.GetEncoderInfo(ImageFormat.Jpeg);
            }
            else if (ext == ".png")
            {
                imageCodecInfo = this.GetEncoderInfo(ImageFormat.Png);
            }
            else if (ext == ".gif")
            {
                imageCodecInfo = this.GetEncoderInfo(ImageFormat.Gif);
            }

            // Create an Encoder object for the Quality parameter.
            Encoder encoder = Encoder.Quality;

            // Create an EncoderParameters object. 
            EncoderParameters encoderParameters = new EncoderParameters(1);

            // Save the image as a JPEG file with quality level.
            EncoderParameter encoderParameter = new EncoderParameter(encoder, quality);
            encoderParameters.Param[0] = encoderParameter;
            newImage.Save(uploadsdir + fileName, imageCodecInfo, encoderParameters);

            return sourceRatio;
        }
}
private ImageCodecInfo GetEncoderInfo(ImageFormat format)
        {
            return ImageCodecInfo.GetImageDecoders().SingleOrDefault(c => c.FormatID == format.Guid);
        }

Call the code above with the following parameters as below, keeping the following points in mind:

  • SaveImages method below is created keeping in mind, it is written for a WebAPI and the image may be received in the byte array format.
  • Use a very large height value, so the new image is re-sized restricted to new width size for thumb, medium and large sizes based on the aspect ratio.
  • The Quality category specifies the level of compression for an image. When used to construct an EncoderParameter, the range of useful values for the quality category is from 0 to 100. The lower the number specified, the higher the compression and therefore the lower the quality of the image. Zero would give you the lowest quality image and 100 the highest.
public string SaveImages(byte[] imgArr, string filename, string extension, string uploadtype, out float ratio)
        {
            const int THUMB_WIDTH = 120;
            const int MEDIUM_WIDTH = 400;
            const int LARGE_WIDTH = 900;
            const int IMG_HEIGHT = 100000;
            const int IMG_QUALITY = 100;
            ratio = 0.0f;
            string[] extensionwhitelist = new string[] { ".jpg", ".jpeg", ".gif", ".png" };

            ImageUploader uploader = new ImageUploader(uploadtype);
            
            var encfilename = encryptfilename(filename);
            try
            {
                if (!extensionwhitelist.Contains(extension))
                {
                    throw new Exception("Invalid File format.");
                }
                
                TypeConverter tc = TypeDescriptor.GetConverter(typeof(Bitmap));
                Bitmap bitmapImg = (Bitmap)tc.ConvertFrom(imgArr);

                uploader.SaveOriginal(bitmapImg, IMG_QUALITY, encfilename + extension, extension);
                uploader.Save(bitmapImg, THUMB_WIDTH, IMG_HEIGHT, IMG_QUALITY, "thumb_" + encfilename + extension, extension);
                uploader.Save(bitmapImg, MEDIUM_WIDTH, IMG_HEIGHT, IMG_QUALITY, "medium_" + encfilename + extension, extension);
                ratio = uploader.Save(bitmapImg, LARGE_WIDTH, IMG_HEIGHT, IMG_QUALITY, "large_" + encfilename + extension, extension);
            }
            catch(Exception ex)
            {
                throw new BusinessServiceException("SaveImages", "Failed saving images.", ex);
            }

            return encfilename;
        }

The method encryptfilename can be written using a method of choice for encrypting the filename of the image.

TLS version handling with http web request CSharp

Since you’re reading this, probably your C# code broke the connection to the website or a third-party API you’re hitting using the HttpWebRequest. The below code shows one such scenario where my application code broke which is running in .net framework 4.0. The company running the API upgraded their security with the TLS version upgrade to 1.2.

The exception that you’re seeing as below while trying to call GetResponse():

The request was aborted: Could not create SSL/TLS secure channel.

Uri url = new Uri(Link);
HttpWebRequest http = (HttpWebRequest)WebRequest.Create(url.ToString());
HttpWebResponse resp = (HttpWebResponse)http.GetResponse();
returnValue = Convert.ToInt32(resp.StatusCode);

HTTPS relies on a family of lower level security protocol implementations called transport level security (TLS), each using different cryptographic algorithms. Transport Layer Security (TLS) is a cryptographic protocol used to establish a secure communications channel between two systems. Anything that is using TLS standard below TLS 1.2 is considered to be non secure because these older encryption algorithms have been cracked at some point. The TLS standards keep evolving and TLS 1.3 is in the works.

Each .net framework supports TLS version 1.2 in the following ways:

  • .Net 4.5 and above: Add the below line of code before making the web request in your code. Some blogs say .Net 4.6 and above support it by default and no code changes are required but as I tried it myself, it doesn’t work.
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12
  • .Net 4.0: This framework does not support the enumeration as in the latest frameworks, the below line of code helps achieve that:
ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;

Older frameworks do no support the latest TLS version, so it’s better to upgrade your application. Also, as a good security practice do not use the fallback code as shown below:

System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;

Another good point to consider upgrading your application is to check if Microsoft still supports the .Net framework you’re using.

Show some love for the pit in my PayPal account.