Download as pdf or txt
Download as pdf or txt
You are on page 1of 66

Selenium WebDriver Recipes in C#,

Third Edition Courtney Zhan


Visit to download the full and correct content document:
https://ebookmass.com/product/selenium-webdriver-recipes-in-c-third-edition-courtne
y-zhan/
Editor
Zhimin Zhan

Author
Courtney Zhan

Selenium WebDriver Recipes in C#


Practical Testing Solutions for Selenium
WebDriver
3rd ed.
Editor
Zhimin Zhan
Brisbane, QLD, Australia

Author
Courtney Zhan
Fitzgibbon, QLD, Australia

ISBN 979-8-8688-0022-1 e-ISBN 979-8-8688-0023-8


https://doi.org/10.1007/979-8-8688-0023-8

© Courtney Zhan, Edited by Zhimin Zhan 2015, This book's irst


edition was published by the author on CreateSpace., 2024

This work is subject to copyright. All rights are solely and exclusively
licensed by the Publisher, whether the whole or part of the material is
concerned, speci ically the rights of translation, reprinting, reuse of
illustrations, recitation, broadcasting, reproduction on micro ilms or in
any other physical way, and transmission or information storage and
retrieval, electronic adaptation, computer software, or by similar or
dissimilar methodology now known or hereafter developed.

The use of general descriptive names, registered names, trademarks,


service marks, etc. in this publication does not imply, even in the
absence of a speci ic statement, that such names are exempt from the
relevant protective laws and regulations and therefore free for general
use.

The publisher, the authors, and the editors are safe to assume that the
advice and information in this book are believed to be true and
accurate at the date of publication. Neither the publisher nor the
authors or the editors give a warranty, expressed or implied, with
respect to the material contained herein or for any errors or omissions
that may have been made. The publisher remains neutral with regard
to jurisdictional claims in published maps and institutional af iliations.

This Apress imprint is published by the registered company APress


Media, LLC, part of Springer Nature.
The registered company address is: 1 New York Plaza, New York, NY
10004, U.S.A.
This book is dedicated to my mum, Xindi.
Preface
It has been nearly eight years since Selenium WebDriver Recipes for C#
Third Edition was published. Since then,
Selenium WebDriver v2 → v4
Visual Studio 2013 → 2022
The uprising of VS Code
Browser market changes mean IE and Edge (classic) are gone.
It is about time to update this book.
There is one observation we want to point out. Before starting this
edition, we opened the recipes and ran all tests in Visual Studio 2022,
and 138 out of 171 tests passed. Please note this was Selenium
WebDriver v2 (from 8 years ago) running against the latest Chrome
browser (v114). The majority of the test failures were related to the
changes in external websites and Firefox tests (which were not
con igured). This shows the high stability of WebDriver.

What’s New
Selenium’s new features and improvements include the following:
DOM shadow roots
Relative locators
Chrome DevTools
New APIs
…and more!
What’s new in this book:
New (about 15% more) and updated recipes
Using Visual Studio Code as the tool for developing/executing test
scripts
Executing Selenium C# tests in a BuildWise continuous testing
server

Who Should Read This Book


This book is for software testers or programmers who are writing (or
want to learn how to write) automated tests with Selenium WebDriver.
To get the most out of this book, basic C# coding skill is required.

How to Read This Book


Typically, a recipe book serves as a reference, allowing readers to
navigate directly to the speci ic topic of interest. For instance, if you
encounter a challenge while testing a multiple select list and require
guidance, you can refer to the Table of Contents and locate the relevant
chapter. This book supports this style of reading. Since the recipes are
arranged according to their levels of complexity, you will also be able to
work through the book from the front to back if you are looking to learn
test automation with Selenium WebDriver.

Recipe Test Scripts


To help you learn more effectively, this book has a dedicated site1 that
contains the recipe test scripts and related resources.
As the old saying goes, “There’s more than one way to skin a cat.”
When it comes to achieving the desired testing outcomes, there are
often multiple approaches. The recipe test scripts in this book are
written for simplicity, so there is always room for improvement. But for
many, understanding the solution quickly and getting the job done are
probably more important.
If you have a better and simpler way, please let us know. All recipe
test scripts are Selenium WebDriver 4 compliant and can be executed
on Chrome, Firefox, and Edge on multiple platforms.

Send Us Feedback
We appreciate your comments, suggestions, and reports on errors in
the book and the recipe test scripts. You may submit your feedback on
the book site.

Courtney Zhan and Zhimin Zhan


September 2023
Any source code or other supplementary material referenced by the
author in this book is available to readers on GitHub
(https://github.com/Apress). For more detailed information, please
visit https://www.apress.com/gp/services/source-code.
Table of Contents
Chapter 1: Introduction
Selenium WebDriver
Selenium Language Bindings
Set Up the Development Environment - Visual Studio Code
Prerequisites
Set Up Visual Studio Code
Cross-Browser Testing
Chrome
Firefox
Safari
Edge
Internet Explorer
Visual Studio Unit Testing Framework
Visual Studio Unit Testing Framework Fixtures
Alternative Framework: NUnit
Run Recipe Scripts
Run Test(s) in VS Code
Find the Test Case
Run an Individual Test Case
Run All Test Cases in a Test Script File
Run Tests from the Command Line
Chapter 2: Locating Web Elements
Start a Browser
Inspect a Web Element in a Browser
Find an Element by ID
Find an Element by Name
Find an Element by Link Text
Find an Element by Partial Link Text
Find an Element by XPath
Find an Element by a Tag Name
Find an Element by Class
Find an Element by CSS Selector
Find an Element by Relative Locators
Chain FindElement to Find Child Elements
Find Multiple Elements
Locate a Web Element That Disappears After Inspect
Chapter 3: Hyperlink
Click a Link by Text
Click a Link by ID
Click a Link by Partial Text
Click a Link by XPath
Click the Nth Link with Exact Same Label
Click the Nth Link by CSS Selector
Verify If a Link Is Present or Not
Get Link Data Attributes
Test If a Link Opens a New Browser Window
Chapter 4: Button
Click a Button by Text
Click a Form Button by Text
Submit a Form
Click a Button by ID
Click a Button by Name
Click an Image Button
Click a Button via JavaScript
Assert If a Button Is Present
Assert If a Button Is Enabled or Disabled
Chapter 5: TextField and TextArea
Enter Text into a Text Field by Name
Enter Text into a Text Field by ID
Enter Text into a Password Field
Clear a Text Field
Enter Text into a Multiline Text Area
Assert a Value
Focus on a Control
Set a Value to a Read-Only or Disabled Text Field
Set and Assert the Value of a Hidden Field
Chapter 6: Radio Button
Select a Radio Button
Clear the Radio Option Selection
Assert a Radio Option Is Selected
Iterate Radio Buttons in a Radio Group
Click the Nth Radio Button in a Group
Click a Radio Button by the Following Label
Customized Radio Buttons with iCheck
Chapter 7: CheckBox
Check by Name
Check by Id
Uncheck a Checkbox
Assert a Checkbox Is Checked (or Not)
Chain FindElement to Find Child Elements
Customized Checkboxes using iCheck
Chapter 8: Select List
Import Selenium.Support
Select an Option by Text
Select an Option by a Value
Select an Option by an Index
Send Keys to the Select Element
Select an Option by Iterating All Options
Select Multiple Options
Clear One Selection
Clear All Selections
Assert a Selected Option
Assert a Label or Value of a Select List
Assert Multiple Selections
Chapter 9: Navigation and Browser
Go to a URL
Visit Pages Within a Site
Open Browser in a Certain Size
Maximize the Browser Window
Move the Browser Window
Minimize a Browser Window
Scroll Focus to Control via JavaScript
Scroll with Scroll Wheel Actions
Switch Between Browser Windows or Tabs
New Browser Window or Tab
Open and Close Browser Tabs Using JavaScript
Remember Current Web Page URL and Come Back to It Later
Chapter 10: Assertion
Assert a Page Title
Assert Page Text
Assert the Page Source
Assert Label Text
Assert Span Text
Assert Div Text or HTML
Assert Table Text
Assert Text in a Table Cell
Assert Text in a Table Row
Assert If an Image Is Present
Assert Element Location and Width
Assert Element CSS Style
Assert JS Errors on a Web Page
Chapter 11: Frames
Testing Frames
Find a Frame with FindElement
Test an IFrame
Test Multiple IFrames
Chapter 12: Testing AJAX
Pause for a Speci ic Duration of Time
Explicit Waits Until Time Out
Implicit Waits Until Time Out
Fluent Waits Until Time Out
Create Your Own Polling Check Function
Refactor with a Reusable Function
Wait for the AJAX Call to Complete Using JQuery
Chapter 13: File Upload and Popup Dialogs
File Upload
File Upload with a Relative Path
JavaScript Popups
Handle JavaScript Popups Using Alert API
Handle JavaScript Popups with JavaScript
Modal Style Dialogs
Timeout on an Operation
Popup Handler Approach
Basic or Proxy Authentication Dialog
Bypass Basic Authentication by Embedding the Username and
Password in the URL
Internet Explorer Modal Dialog
Chapter 14: Debugging Test Scripts
Print Text for Debugging
Write the Page Source or Element HTML into a File
Take a Screenshot
Leave the Browser Open After a Test Finishes
Debug Test Execution using Debuggers
Enable Breakpoints
Execute One Test Case in Debugging Mode
Step Over Test Execution
Chapter 15: Test Data
Get Data Dynamically
Get a Random Boolean Value
Get a Random Boolean Value with Timestamps
Generate a Number Within a Range
Get a Random Character
Get a Random String at a Fixed Length
Get a Random String in a Collection
Generate a Test File at a Fixed Size
Retrieve Data from a Database
Chapter 16: Browser Pro ile and Capabilities
Get Browser Type and Version
Set the Page Load Strategy
Set an HTTP Proxy for a Browser
Verify File Download in Chrome
Verify File Download in Firefox
Bypass Basic Authentication with the Firefox AutoAuth Plugin
Manage Cookies
Headless Browser Testing with Chrome
Test Responsive Websites
Chapter 17: Advanced User Interactions
Double-Click a Control
Move the Mouse to a Control (Mouse Over)
Click and Hold (Select Multiple Items)
Context Click (Right-Click a Control)
Drag-and-Drop
Drag a Slider
Send Key Sequences (Select All and Delete)
Chapter 18: HTML5 and JavaScript
HTML5 Email Type Field
HTML5 Time Field
Invoke the onclick JavaScript Event
Invoke JavaScript Events Such As OnChange
Scroll to the Bottom of a Page
Chosen - Standard Select
Chosen - Multiple Select
AngularJS Web Pages
Ember JS Web Pages
Faking Geolocation with JavaScript
Save an SVG Chart to a PNG Image
Save a Canvas to a PNG Image
Chapter 19: WYSIWYG HTML Editors
TinyMCE
CKEditor
SummerNote
CodeMirror
Chapter 20: Leveraging Programming
Throw Exceptions to Fail Tests
Ignorable Test Statement Error
Read an External File
Data-Driven Tests with Excel
Data-Driven Tests with CSV
Identify Element IDs with Dynamically Generated Long Pre ixes
Sending Special Keys Such as Enter to an Element or Browser
Use of Unicode in Test Scripts
Extract a Group of Dynamic Data: Verify Search Results in Order
Verify Uniqueness of a Set of Data
Extract Dynamic Visible Data Rows from a Results Table
Extract Dynamic Text Following a Pattern Using Regex
Chapter 21: Optimization
Assert Text in a Page Source Is Faster Than the Text
Getting Text From a More Speci ic Element Is Faster
Avoid Programming If-Else Blocks If Possible
Use a Variable to Cache Unchanged Data
Enter Large Text into a Text Box
Use Environment Variables to Change Test Behaviors
Dynamically
Testing a Web Site in Multiple Languages
Multi-Language Testing with Lookups
Chapter 22: Gotchas
Test Starts Browser But No Execution but a Blank Screen
Failed to Assert Copied Text in Browser
The Same Test Works for Chrome, But Not for IE
“unexpected tag name ‘input’”
Element Is Not Clickable or Visible
Chapter 23: Selenium 4
Chrome For Testing
New Browser Window or Tab
Basic Authentication via Register
Print to PDF
Save Element Screenshot
Drive Elements Inside a Shadow DOM
Relative Locators
Get the Element on the Right of a Checkbox
Get the Element(s) on the Left of a Checkbox
Above in a Table
Below in a Table
Near
Summary
Chapter 24: Selenium DevTools
Emulate Browser Crash
CDP Send Command
Emulate GEO Location
Emulate Locale
Emulate Timezone
Network Interception
Network Latency
Security: Ignore Certi icateErrors
Chapter 25: Selenium Grid
Selenium Server Standalone
Execute Tests on a Remote Machine
Set Up Selenium Grid
Using Selenium Grid to Run Tests
Issues with Selenium Grid
Chapter 26: Continuous Testing
CI vs. CT
Preparation
Source Control Test Scripts in Git
Compile Test Scripts
Execute a Test Script from the Command Line
Execute a Test Script with JUnit XML
Set Up BuildWise Server
Set Up a Build Project
Run a Suite of C# tests in BuildWise
What’s the Magic?
Chapter 27: Case Studies
Verify Chart Generation
Test Design
Test Steps
Test Script
Drawing on a Canvas
Test Steps
Test Script
Automated Testing Elements on a Lazy Load Page
Test Design
Test Steps
Generate a User Guide with an Automation Script
Test Design
Test Steps
Generated Files
What Does It Look Like?
Summary
Broken Link Checker
Test Design
Test Steps
Afterword
Resources
Appendix 1: Visual Studio IDE Setup
Appendix 2: Visual Studio IDE Debugging Test Execution
Index
About the Authors
Courtney Zhan
is a software development engineer at
Amazon Australia. She is passionate
about programming, end-to-end test
automation, continuous testing, and
graphic design. She shares tips and
guides on test automation and
continuous testing weekly on Medium at
courtneyzhan.medium.com.

Zhimin Zhan
is the founder and CTO of AgileWay Pty
Ltd, Australia. As an advisor and coach,
he helps organizations implement
continuous testing with open
technologies such as Selenium
WebDriver and Appium. Zhimin is the
creator of the TestWise Testing IDE and
international award-winning BuildWise
CT Server and an author.
Footnotes
1 http://zhimin.com/books/selenium-recipes-csharp
© Courtney Zhan, Edited by Zhimin Zhan 2024
Z. Zhan (ed.), Selenium WebDriver Recipes in C#
https://doi.org/10.1007/979-8-8688-0023-8_1

1. Introduction
Courtney Zhan1
(1) Fitzgibbon, QLD, Australia

Selenium WebDriver is a free and open-source library for automating web applications. Basically,
you can use it to drive web apps in a browser and for testing purposes.

Selenium WebDriver
Selenium WebDriver is a merger of Selenium v1 and another automation framework called
WebDriver led by Simon Stewart at Google. (Simon later joined Facebook and Apple.) Selenium 2.0
was released in July 2011. We use “Selenium,” “WebDriver,” and “Selenium WebDriver”
interchangeably in this book. Selenium v3 and v4 were released in October of 2016 and October of
2021, respectively.

Selenium Language Bindings


Selenium scripts can be written in multiple programming languages such as Java, C#, JavaScript,
Python, and Ruby (the core ones). All examples in this book are written in Selenium with C# binding.
As you will see in the examples below, the use of Selenium in different bindings is very similar. Once
you master one, you can apply it to others quite easily. Take a look at a simple Selenium test script in
four different language bindings: C#, Java, Python, and Ruby.
C#:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;

class GoogleSearch
{
static void Main()
{
// Create a new instance of the driver
IWebDriver driver = new ChromeDriver();

// And now use this to visit Google


driver.Navigate().GoToUrl("http://www.google.com");

// Find the text input element by its name


IWebElement query = driver.FindElement(By.Name("q"));

// Enter something to search for


query.SendKeys("Hello Selenium WebDriver!");
// Submit the form based on an element in the form
query.Submit();

// Check the title of the page


Console.WriteLine(driver.Title);
}
}

Java:

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;

public class GoogleSearch {


public static void main(String[] args) {
WebDriver driver = new FirefoxDriver();
driver.get("http://www.google.com");
WebElement element = driver.findElement(By.name("q"));
element.sendKeys("Hello Selenium WebDriver!");
element.submit();
System.out.println("Page title is: " + driver.getTitle());
}
}

Python:

from selenium import webdriver

driver = webdriver.Firefox()
driver.get("http://www.google.com")

elem = driver.find_element_by_name("q")
elem.send_keys("Hello WebDriver!")
elem.submit()

print(driver.title)

Ruby:

require "selenium-webdriver"

driver = Selenium::WebDriver.for :chrome


driver.navigate.to "http://www.google.com"

element = driver.find_element(:name, 'q')


element.send_keys "Hello Selenium WebDriver!"
element.submit

puts driver.title

Set Up the Development Environment - Visual Studio Code


Both the free Visual Studio Code (VS Code, for short) and the traditional Visual Studio IDE
(Community or Enterprise edition) can be used to develop and execute C# Selenium tests.
VS Code is a popular, if not the most popular, code editor and is more lightweight than the Visual
Studio IDE. The Visual Studio IDE is better suited for development work. If you are only doing
testing, VS Code is more than enough. We will use VS Code in this book. If you would like to use
Visual Studio IDE, see the Appendix for setup instructions.

Prerequisites
Download and install VS Code.
A browser, such as Chrome or Firefox
Have the browser’s driver (such as the ChromeDriver executable) included in the PATH.

Set Up Visual Studio Code


1.
Install the .NET SDK.
Download it from https://dotnet.microsoft.com/en-
us/download/dotnet/7.01. Note that .NET SDK now works on all major platforms. We
chose macOS X64 since we were using a Mac. Obviously, it works on Windows too.
Run the installer (Figure 1-1).

Figure 1-1 .NET SDK Installer screenshot

Verify it was installed correctly by running this code in a new terminal window:

$ dotnet --version
7.0.202

2.
Install the C# and NuGet package manager extensions into VS Code.
Click the Extensions tab on the left pane in VS Code (or use Command + Shift + X on a Mac
or Control + Shift + X on Windows). Search for C# in the Extension Marketplace. This
will add support for C# in VS Code. Install it (Figure 1-2).
Figure 1-2 Visual Studio Code’s C# extension

Repeat the same process for the NuGet Package Manager, which helps to add libraries, such as
Selenium WebDriver, to test projects (Figure 1-3).

Figure 1-3 Visual Studio Code’s NuGet Package Manager extension

3.
Create a MSTest project folder.
The MSTest framework is a test framework which is included by default with Microsoft Visual
Studio.
From the terminal, run the following command to create a project named
HelloSeleniumTest:

$ dotnet new mstest -o HelloSeleniumTest

Sample output:

The template "MSTest Test Project" was created successfully.


Processing post-creation actions...
Restoring
/Users/courtney/tmp/HelloSeleniumTest/HelloSeleniumTest.csproj:
Determining projects to restore...
Restored /Users/courtney/tmp/HelloSeleniumTest/HelloSeleniumTest.csproj
(in 1.16 sec).
Restore succeeded.

4.
Open the newly created test project folder in VS Code.
Run the (empty) test case by clicking the Run Test link, as indicated in Figure 1-4.
Figure 1-4 How to run an empty test in Visual Studio Code

There is an extra compilation step, which was taken care of by VS Code, for readers who have
only used scripting languages, such as Ruby and Python, as C# is a compiled language. As you can
see, HelloSeleniumTest.dll is the output of the compilation (a.k.a. build).
This empty test case is meaningless, but if it passes, it means your MSTest setup (with SDK) is
correct.
5.
Add the Selenium WebDriver package to the test project.
You want to run a Selenium C# test that drives a Chrome browser (on macOS). There are three
prerequisites:
The Chrome browser
The ChromeDriver for the matching browser version is in the PATH
The Selenium WebDriver library must be installed and con igured in the test project.
The irst two are generic and needed for any WebDriver-powered automation. The third one
you can achieve with the NuGet package manager.
Press Command + Shift + P on Mac (or Control + Shift + P on Windows) to
start the Command Palette, type NuGet, and select the NuGet Package Manager: Add Package
option, as shown in Figure 1-5.

Figure 1-5 Executing NuGet Package Manager’s Add Package command in Visual Studio Code

Then type in Selenium and press the Enter key (Figure 1-6).
Figure 1-6 Adding the package Selenium.WebDriver in Visual Studio Code

Press the Enter key to select Selenium.WebDriver. Then select the latest version (in our case,
4.8.2). See Figure 1-7.

Figure 1-7 Selecting Selenium.WebDriver’s package version in Visual Studio Code

After that, you will see popups at the right bottom of VS Code (Figure 1-8).

Figure 1-8 Prompt to restore unresolved dependencies in Visual Studio Code

Click the Restore button.


6. First Selenium C# test
Add a new ile, LoginTest.cs, to the project (in VS Code). Paste in this content:

namespace HelloSeleniumTest;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;

[TestClass]
public class SeleniumLoginOkTest
{
IWebDriver driver = null;
[TestMethod]
public void TestLoginOK()
{
driver = new ChromeDriver();
driver.Navigate().GoToUrl("http://travel.agileway.net");
driver.FindElement(By.Name("username")).SendKeys("agileway");
driver.FindElement(By.Name("password")).SendKeys("testwise");
driver.FindElement(By.Name("password")).Submit();
Assert.IsTrue(driver.PageSource.Contains("Signed in!"));
driver.Quit();
}
}

Run it (as shown earlier in Step 4). You will see a Chrome browser launch and log into the
AgileTravel site.

Cross-Browser Testing
One big advantage of Selenium WebDriver over other web automation frameworks, in our opinions,
is that it supports all major web browsers: Chrome, Safari, Microsoft Edge, and Firefox. To be more
precise, all major browser vendors support WebDriver, a W3C standard.
The browser market nowadays is more diversi ied (based on the StatsCounter2, the usage
shares in May 2023 for Chrome, Safari, Edge, and Firefox are 66.0%, 12.8%, 9.9%, and 5.3%,
respectively). It is logical that all external facing web sites require serious cross-browser testing.
Selenium is a natural choice for this purpose because it far exceeds other web automation tools and
frameworks.

Chrome
To run Selenium tests in Google Chrome, besides the Chrome browser itself, ChromeDriver3 needs
to be installed. Visit https://chromedriver.chromium.org/downloads4 and choose a
version that matches your browser (Figure 1-9).

Figure 1-9 ChromeDriver downloads page

Download the one for your target platform, unzip it, and put the chromedriver executable in
your PATH. To verify the installation, open a command window, execute the command
chromedriver –version. You should see something like Figure 1-10.

Figure 1-10 Verifying the ChromeDriver version via the command line

The following code launches a new Chrome browser window and closes it one second later:

namespace HelloSeleniumTest;
using System;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
public class HelloSelenium {
public static void main(String[] args) {
IWebDriver driver = new ChromeDriver();
System.Threading.Thread.Sleep(1000);
driver.Quit();
}
}

C# beginners might be confused/frustrated when running this simple code snippet, with issues related
to “project,” “namespace,” and “compiling.” Don't worry. We will cover the test execution shortly. For
readers who have used a good scripting language such as Ruby: Yes, test automation with Ruby is a lot
easier.

Firefox
Selenium for Firefox requires geckodriver5. The following test script will open a web site in a new
Firefox window:

using OpenQA.Selenium.Firefox;
// ...
IWebDriver driver = new FirefoxDriver();

Safari
Safari is the default macOS web browser. While Safari supports WebDriver out of the box, you will
need to do minor setup to enable automation.
1.
Enable Developer Tools in Safari.
Open Safari’s Preferences (Safari ➤ Settings on the top bar or Command + ,).
In the Advanced tab, check the “Show Develop menu in menu bar” box. See Figure 1-11.

Figure 1-11 Enabling the Develop menu in Safari’s settings

You should see the Develop menu in the top bar of Safari now.
2. Enable Remote Automation in Safari.
Under the Develop menu tab, check the “Allow Remote Automation” option (Figure 1-12).
Figure 1-12 Enabling Remote Automation in Safari’s menu bar

using OpenQA.Selenium.Safari;
// ...
IWebDriver driver = new SafariDriver();

Edge
Edge is Microsoft’s new default web browser on Windows 10+. The current Edge is based on
Chromium (the legacy version was deprecated in March 2021). To drive Edge with WebDriver, you
need Microsoft Edge WebDriver6. The installation process is the same as ChromeDriver, and the ile
is msedgedriver.exe.

using System.IO;
using OpenQA.Selenium.Edge;
// ...
IWebDriver driver = new EdgeDriver();

Internet Explorer
Internet Explorer has retired. In a rare case that you need to test a certain website against IE, it is
still possible. Selenium requires IEDriverServer to drive IE browser. Its installation process is very
similar to ChromeDriver. IEDriverServer is available at
https://www.selenium.dev/downloads7. Choose the right one based on your Windows
version (32- or 64-bit).

using OpenQA.Selenium.IE;
//...
IWebDriver driver = new InternetExplorerDriver();

Visual Studio Unit Testing Framework


The script examples above drive browsers. Strictly speaking, they are not tests (Selenium is an
automation framework). To make the effective use of Selenium scripts for testing, you need to put
them in a test framework that de ines test structures and provides assertions (performing checks in
test scripts). Here is an example using Visual Studio Unit Testing Framework:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.IE;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Edge;
using OpenQA.Selenium.Safari;
using System.Collections.ObjectModel;

[TestClass]
public class GoogleSearchDifferentBrowsersTest {

[TestMethod]
public void TestInIE() {
IWebDriver driver = new InternetExplorerDriver();
driver.Navigate().GoToUrl("https://agileway.com.au/demo");
System.Threading.Thread.Sleep(1000);
driver.Quit();
}

[TestMethod]
public void TestInFirefox() {
IWebDriver driver = new FirefoxDriver();
driver.Navigate().GoToUrl("https://agileway.com.au/demo");
System.Threading.Thread.Sleep(1000);
driver.Quit();
}

[TestMethod]
public void TestInChrome() {
IWebDriver driver = new ChromeDriver();
driver.Navigate().GoToUrl("https://agileway.com.au/demo");
System.Threading.Thread.Sleep(1000);
driver.Quit();
}

[TestMethod]
public void TestInSafari() {
IWebDriver driver = new SafariDriver();
driver.Navigate().GoToUrl("https://agileway.com.au/demo");
System.Threading.Thread.Sleep(1000);
driver.Quit();
}

[TestMethod]
public void TestInEdge() {
// Default option, MicrosoftWebDriver.exe must be in PATH
IWebDriver driver = new EdgeDriver();
driver.Navigate().GoToUrl("https://agileway.com.au/demo");
System.Threading.Thread.Sleep(1000);
driver.Quit();
}
}

[TestMethod] annotates a test case below, in a format of TestCapitalCase. You will ind
more about VS Unit Test Framework from its home page8. However, we honestly don’t think it is
necessary. The part used for test scripts is not much and is quite intuitive. After studying and trying
out some examples, you will be quite comfortable with it.

Visual Studio Unit Testing Framework Fixtures


If you worked with xUnit before, you must know the setUp() and tearDown() ixtures, which are
called before or after every test. In VS Unit Test Framework, by using annotations
([ClassInitialize], [TestInitialize], [TestCleanup], and [ClassCleanup]), you
can choose the name for the ixtures. Here is an example:

[ClassInitialize]
public static void BeforeAll() {
// run before all test cases
}

[TestInitialize]
public void Before() {
// run before each test case
}

[TestMethod]
public void TestCase1() {
// one test case
}

[TestMethod]
public void TestCase2() {
// another test case
}

[TestCleanup]
public void After() {
// run after each test case
}

[ClassCleanup]
public static void AfterAll() {
// run after all test cases, typically, close browser
}

Please note that [ClassCleanup] does not guarantee all tests from one test class have been
executed before a new test class is initialized. What does this mean for your execution? If you run
one test case or all test cases in a single test class, [ClassCleanup] is invoked, it will run as you
expected. However, when you run several test classes in one go, when [ClassCleanup] is
invoked, it is nondeterministic (which could lead to many opened browser windows). This is not
good. Read this MSDN blog post for explanation: “ClassCleanup May Run Later Than You Think.”9

Alternative Framework: NUnit


NUnit10, inspired by JUnit, is an open-source unit testing framework for Microsoft .NET. If you have
used JUnit before, you will ind similarities in NUnit. Compared to VSTest, NUnit is more lexible for
executing tests from the command line with JUnit style test reports (which can be easily integrated
with CI Servers).
However, you will need to spend some effort getting NUnit to work with Visual Studio.

Run Recipe Scripts


Test scripts for all recipes can be downloaded from this book’s site. They are all in the ready-to-run
state. We include the target web pages as well as the Selenium test scripts. There are two kinds of
target web pages: local HTML iles and web pages on a live site. Running the tests written for live
sites requires an Internet connection.

Run Test(s) in VS Code


The most convenient way to run one test case or a test suite is to do it in an IDE. When you have a
large number of test cases, the most effective way to run all tests is done by a continuous testing
process, which we cover in a later chapter.

Find the Test Case


You can locate the recipe either by following the chapter or searching by name. There can be
hundreds of test cases in one test project. Being able to quickly navigate to the test case is
important when your test suite is large.
Visual Studio Code has a Navigate To Symbol command; its default keyboard shortcut is
Command + T on Mac (or Control + T on Windows). See Figure 1-13.

Figure 1-13 Visual Studio Code’s Navigate To Symbol search box

A pop-up window lists all artifacts (test methods and classes) in the project for your selection.
The search starts as soon as you type, as you can see in Figure 1-14.

Figure 1-14 Visual Studio Code’s Navigate To Symbol search box jumping to a speci ic test

Run an Individual Test Case


Under the [TestMethod] annotation, there is an option called Run Test. Click this button to run
the single test case (see Figure 1-15).
Figure 1-15 The Run Test button in Visual Studio Code

Figure 1-16 shows a screenshot of the execution panel where one test case has passed.

Figure 1-16 Visual Studio Code execution panel after executing a test

Run All Test Cases in a Test Script File


You can also run all test cases in the currently opened test script ile by clicking the Run All
Tests button under the [TestClass] annotation, as shown in Figure 1-17.

Figure 1-17 The Run All Tests (in the current ile) button in Visual Studio Code

Figure 1-18 is a screenshot of the execution panel where all test cases in a test script ile have
passed.

Figure 1-18 Visual Studio Code execution panel after executing tests

Run Tests from the Command Line


One key advantage of open-source test automation frameworks, such as Selenium, is FREEDOM. You
can edit the test scripts in any text editor and run them from a command line. Here are two reasons
why a test automation engineer must know how to execute tests from command line:
You can eliminate issues from tools.
It’s required for continuous testing because the CT server invokes test executions this way.
To run a C# class, you need to compile it irst (if you’re using an IDE, this is usually done
automatically before execution).
1. Open a command prompt.
You can use the standard command window (cmd.exe) or PowerShell. Also, most IDEs
(including VS Code) provide an interface for the terminal. In VS code, you jump to the terminal
with Control + `.
2.
Change directory to your solution/working folder.

cd C:\books\SeleniumRecipes-C#\recipes\SeleniumRecipes

3.
Run all test cases using the dotnet test in one test script (class).
Run all tests in a ile with dotnet test -- ilter NameSpace.TestClass.

dotnet test --filter


SeleniumRecipes.GoogleSearchDifferentBrowsersTest

4.
Run an individual test case.
To only run a single test case, use NameSpace.TestClass.TestMethod instead. For
example:

dotnet test --filter


SeleniumRecipes.GoogleSearchDifferentBrowsersTest.TestInFirefox

To run multiple test cases, use double quotations around the ilter and the OR operator (|) to
separate test classes:

dotnet test --filter


"SeleniumRecipes.GoogleSearchDifferentBrowsersTest.TestInFirefox |
SeleniumRecipes.GoogleSearchDifferentBrowsersTest.TestInChrome"

Sample output:

Starting test execution, please wait...


A total of 1 test files matched the specified pattern.

Passed! - Failed: 0, Passed: 2, Skipped: 0, Total: 2, Duration:


12 s
- SeleniumRecipesCSharp.dll (net7.0)

Footnotes
1 https://dotnet.microsoft.com/en-us/download/dotnet/7.0

2 http://gs.statcounter.com/

3 http://chromedriver.storage.googleapis.com/

4 https://chromedriver.chromium.org/downloads

5 https://github.com/mozilla/geckodriver
6 https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/

7 www.selenium.dev/downloads

8 http://msdn.microsoft.com/en-us/library/ms243147

9 http://blogs.msdn.com/b/ploeh/archive/2007/01/06/classcleanupmayrunlaterthanyouthink.aspx

10 http://nunit.org/
© Courtney Zhan, Edited by Zhimin Zhan 2024
Z. Zhan (ed.), Selenium WebDriver Recipes in C#
https://doi.org/10.1007/979-8-8688-0023-8_2

2. Locating Web Elements


Courtney Zhan1
(1) Fitzgibbon, QLD, Australia

As you may have already igured out, to drive an element in a page, you need to ind it irst. Selenium uses
locators to ind and match elements on web page. There are eight locators in Selenium, shown in Table 2-1.
Table 2-1 Selenium Locators

Table Example
ID FindElement(By.Id("user"))
Name FindElement(By.Name("username"))
Link Text FindElement(By.LinkText("Login"))
Partial Link Text FindElement(By.PartialLinkText("Next"))
Xpath FindElement(By.Xpath("//div[@id='login']/input"))
Tag Name FindElement(By.TagName("body"))
Class Name FindElement(By.ClassName("table"))
CSS FindElement(By.CssSelector, "#login > input[type="text"]"))

Selenium WebDriver 4 (released in October 2021) added a new locator type called Relative, which we
don’t ind that useful, yet. We include one simple example use of one relative locator in this chapter and
more in Chapter 25.
You may use any one of the locators to narrow down the element you are looking for.

Start a Browser
Testing websites starts with a browser.

static WebDriver driver = new ChromeDriver();


driver.Navigate().GoToUrl("https://agileway.com.au/demo")
// or
driver.Url = "https://agileway.com.au/demo";

Use ChromeDriver, SafariDriver, EdgeDriver, and FirefoxDriver for testing in Chrome,


Safari, Edge, and Firefox, respectively.

Note: Test Site We prepared the test pages for the recipes. You can download them (in a zip ile) from
the book’s site. Unzip to a local directory and refer to test pages like this:

siteUrl = "file:///Users/courtney/work/books/SeleniumRecipes-
C%23/site";driver.Navigate().GoToUrl(siteUrl + "/button.html");

For beginners, we recommend closing the browser window at the end of a test class.

driver.Quit();
There is also driver.Close(), which closes the browser window with the focus. driver.Quit()
closes all browser windows and ends the WebDriver session.

Inspect a Web Element in a Browser


Note Every web page is set by its page source in HTML.

A simple way to ind the HTML fragment for a web element is to right-click the web element in a Chrome
browser and select Inspect, as shown in Figure 2-1.

Figure 2-1 Inspect mode on the Chrome browser

Then choose an appropriate Selenium locator based on the HTML fragment. It is not hard, and you don’t
even have to know HTML well. After some trial and error (like the recipes in this book), you will get it quite
quickly. For example, for the text box in Figure 2-1, name="comment" can be used to locate it.

Find an Element by ID
Using an ID is the easiest and safest way to locate an element in HTML, if it exists. For a web page that is
W3C HTML compliant1, the IDs should be unique and identi ied in web controls. In comparison to texts, test
scripts that use IDs are less prone to application changes (e.g., developers may decide to change the label,
but are less likely to change the ID).

driver.FindElement(By.Id("submit_btn")).Click(); // Button
driver.FindElement(By.Id("cancel_link")).Click(); // Link
driver.FindElement(By.Id("username")).SendKeys("agileway"); // Textfield
driver.FindElement(By.Id("alert_div")).getText(); // HTML Div element

Find an Element by Name


The name attributes are used in form controls such as text ields and radio buttons. The values of the name
attributes are passed to the server when a form is submitted. In terms of the least likelihood of a change, the
name attribute is probably as good as an ID.

driver.Navigate().GoToUrl(TestHelper.SiteUrl() + "/locators.html");

driver.FindElement(By.Name("comment")).SendKeys("Selenium Cool");

Find an Element by Link Text


For hyperlinks only, using a link’s text is probably the most direct way to click a link because it is what you
see on the page.

driver.FindElement(By.LinkText("Cancel")).Click();

Find an Element by Partial Link Text


Selenium allows you to identify a hyperlink control with partial text. This can be quite useful when the text
is dynamically generated. In other words, the text on one web page might be different on your next visit. You
might be able to use the common text shared by these dynamically generated link texts to identify them.
// will click the "Cancel" link
driver.FindElement(By.PartialLinkText("ance")).Click();

Find an Element by XPath


XPath, the XML Path Language, is a query language for selecting nodes from an XML document. When a
browser renders a web page, it parses it into a DOM tree or similar. XPath can be used to refer to a certain
node in the DOM tree. If this sounds a little too technical for you, don’t worry; just remember that XPath is
the most powerful way to ind a speci ic web control in HTML.

// clicking the checkbox under 'div2' container


driver.FindElement(By.XPath("//*
[@id='div2']/input[@type='checkbox']")).Click();

Some testers feel intimidated by the complexity of XPath. However, in practice, there is only a limited
scope of XPath to master for testers.

AVOID USING COPIED XPATH FROM A BROWSER’S DEVELOPER TOOL


The browser’s developer tool (right-click to select the Inspect element to show it) is very useful for
identifying a web element in a web page. You may get the XPath of a web element there, as shown in
Figure 2-2 (in Chrome).

Figure 2-2 Copying the XPath of an element

The copied XPath for the second Click here link in the example is as follows:

//*[@id="container"]/div[3]/div[2]/a

It works. However, we do not recommend this approach because the test script is fragile. If a
developer later adds another div under <div id="container">, the copied XPath will no longer be
correct for the element, while //div[contains(text(), "Second")]/a[text()="Click
here"] still works.
In summary, XPath is a very powerful way to locate web elements when Id, Name, or LinkText are
not applicable. Try to use an XPath expression that is less vulnerable to structure changes around the
web element.

Find an Element by a Tag Name


There are a limited set of tag names in HTML. In other words, many elements share the same tag names on
a web page. You normally don’t use the tag_name locator by itself to locate an element. You often use it
with others in chained locators (see the section below). However, there is an exception:

driver.FindElement(By.TagName("body")).Text;

The above test statement returns the text view of a web page. This is a very useful one because Selenium
WebDriver does not have a built-in method to return the text of a web page.

Find an Element by Class


The class attribute of an HTML element is used for styling. It can also be used for identifying elements.
Commonly, an HTML element’s class attribute has multiple values, like so:

<a href="back.html" class="btn btn-default">Cancel</a>


<input type="submit" class="btn btn-deault btn-primary">Submit</input>

You may use any one of them.

driver.FindElement(By.ClassName("btn-primary")).Click(); // Submit button


driver.FindElement(By.ClassName("btn")).Click(); // Cancel
link

// the below will return error "Compound class names not permitted"
// driver.FindElement((By.ClassName("btn btn-deault btn-primary")).Click();

The ClassName locator is convenient for testing JavaScript/CSS libraries (such as TinyMCE), which
typically use a set of de ined class names.

// inline editing
driver.FindElement(By.Id("client_notes")).Click();
System.Threading.Thread.Sleep(500);
driver.FindElement(By.ClassName("editable-textarea")).SendKeys("inline
notes");
System.Threading.Thread.Sleep(500);
driver.FindElement(By.ClassName("editable-submit")).Click();

Find an Element by CSS Selector


You may also use the CSS path to locate a web element.

driver.FindElement(By.CssSelector("#div2 >
input[type='checkbox']")).Click();

However, the use of a CSS selector is generally more prone to structure changes of a web page.

Find an Element by Relative Locators


The purpose of relative locators is to ind a speci ic element(s) regarding the position of another element.
There was hype about relative locators ( irst introduced in Sahi Pro). However, we have reservations for
two reasons:
We rarely have need for relative locators, so XPath is ine.
The proximity check (used in relative locators) does not always work as expected.
Here are quotes from Simon Stewart’s Selenium 4 master course on TestGuild2:
“The way that proximity works, things are looking like they should be found, but sometimes aren’t.”
“Be aware that relative locators are very sensitive to page layout.”
Here is a simple example:

using static OpenQA.Selenium.RelativeBy;

IWebElement submitBtn = driver.FindElement(By.ClassName("btn"));


IWebElement textField =
driver.FindElement(RelativeBy.WithLocator(By.Name("comment")).Above(submitBtn)

You will ind more examples of using this new relative locators in Chapter 25.

Chain FindElement to Find Child Elements


For a page containing more than one element with the same attributes, like the following one, you can use
the XPath locator.
<div id="div1">
<input type="checkbox" name="same" value="on"> Same checkbox in Div 1
</div>
<div id="div2">
<input type="checkbox" name="same" value="on"> Same checkbox in Div 2
</div>

There is another way: you can chain several FindElement instances to ind a child element.

driver.FindElement(By.Id("div2")).FindElement(By.Name("same")).Click();

Find Multiple Elements


As its name suggests, FindElements returns a list of matched elements back. Its syntax is the same as
FindElement, so you can use any of the eight locators.
The following test statement will ind two checkboxes under div#container and click the second one:

ReadOnlyCollection<IWebElement> checkbox_elems =
driver.FindElements(By.XPath("//div[@id='container']//input[@type='checkbox']"
System.Console.WriteLine(checkbox_elems); // => 2
checkbox_elems[1].Click();

Sometimes FindElement fails due to multiple matching elements on a page, which you were not
aware of. FindElements will come in handy to ind them out.

Locate a Web Element That Disappears After Inspect


Dynamic elements are generated on the ly and often do not exist in the static page source. The only
practical way (we are aware of) is to get them by inspecting the dynamic element directly. However, some
web elements disappear after you select the Inspect menu item (via right-click) because their focus has
been lost.
There are two workarounds:
1.
Open the developer tools in Chrome and select the Elements tab, right-click the parent node of the
element you want to inspect, and in the contextual menu, click Break on ➤ Subtree modi ications
(Figure 2-3).

Figure 2-3 Chrome’s “Break on subtree modi ications” setting

2. One simple workaround (more like a hack, but it works) is to type the following in the browser console:

setTimeout(() => { debugger; }, 5000)


This gives you 5 seconds to check the element source before the debugger shows up. See Figure 2-4.

Figure 2-4 Chrome’s browser debugger mode

Footnotes
1 https://zhiminzhan.medium.com/too-many-failed-javascript-test-automation-frameworks-fed006da05ec

2 https://testguild.com/course/relative-locators
© Courtney Zhan, Edited by Zhimin Zhan 2024
Z. Zhan (ed.), Selenium WebDriver Recipes in C#
https://doi.org/10.1007/979-8-8688-0023-8_3

3. Hyperlink
Courtney Zhan1
(1) Fitzgibbon, QLD, Australia

Hyperlinks (or links) are fundamental elements of web pages. As a matter of fact, hyperlinks make the World
Wide Web possible. A sample link is shown in Figure 3-1 and then we show the HTML source.

Figure 3-1 An example of a hyperlink

HTML source:

<a href="index.html" id="recommend_selenium_link" class="nav" data-id="123"


style="font-size: 14px;">Recommend Selenium</a>

Click a Link by Text


Using text is probably the most direct way to click a link in Selenium, as it is what we see on the page.

driver.FindElement(By.LinkText("Recommend Selenium")).Click();

Please note that using CSS (e.g. text-transform: uppercase;) might change the text view from
its HTML source. In this case, use the text as you see.

Click a Link by ID
driver.FindElement(By.Id("recommend_selenium_link")).Click();

Furthermore, if you are testing a web site with multiple languages, using IDs is probably the most
favorable option. You do not want to write test scripts like the following:

if (isItalian()) {
driver.FindElement(By.LinkText("Accedi")).Click();
} else if (isChinese()) { // a helper function determines the locale
driver.FindElement(By.LinkText, "登录").Click();
} else {
driver.FindElement(By.LinkText("Sign in")).Click();
}

Click a Link by Partial Text


driver.FindElement(By.PartialLinkText("Recommend Seleni")).Click();

Click a Link by XPath


This example inds a link with the text “Recommend Selenium” under a <p> tag:
Another random document with
no related content on Scribd:
Names of the Deceas’d belonging to
Barnſtable.
Capt. John Ruſſell, Barn. Lothrop, Daniel
Hall, Tho. Caſley, Eben. Bacon, Jaſey Garritt,
John Berry, Barnabas Howes, Stephen
Bacon, Jon. Lothrop, Boſton, a Negro Man.
The SHIPWRECK: A Hymn of Praiſe.
Deep calleth unto deep at the noiſe of thy
water-ſpouts: All thy waves and thy
billows are gone over me.—Yet the
L O R D hath ſhewn his loving kindneſs in
the day-time: In the night my song shall
be with him, and my prayer unto the
G O D of my life.—When I remember
theſe things I pour out my ſoul in me, with
the voice of joy and praiſe. David.
W HEN mountain billows o’er me roll
I poured out my troubled ſoul
When waves of adverſe fortune meet,
Thou prov’d a guide unto my feet.
2. My ſong ſhall always be on GOD,
I’m ſav’d by his uplifted rod;
My utmoſt ſearch ſhall be to know
The LORD who ſav’d from froſt and ſnow.
3. LORD may I fear thee all my days,
Thy mercies claim my higheſt praiſe;
For in the time of deep deſpair,
Preſerv’d I was by heav’nly care.
4. The LORD is good to thoſe who call
On Him, and do depend for all;
In adverſe fate if we depend
On GOD, he will deliv’rence ſend.
5. In deep diſtreſs and trouble ſore,
When ſurging billows round me roar,
Altho’ he ſlay me, yet I’ll truſt
On God, my Saviour, who is juſt.
6. Not only truſt but I will call
On Him, who is the L O R D of all;
My day-ſong ſhall in thee accord,
And in the night I’ll praiſe the L O R D.
7. Thy goodneſs and thy tender care
Reliev’d me when in deep deſpair;
Thy gracious and thy friendly Crook
I will record in this my Book.
8. Had it not been for thy controul,
The deep would ſwallow up my ſoul;
Tho’ froſt me of my feet deprive,
Yet I will praiſe thee I’m alive.
9. Alive to praiſe thee, O my G O D,
Tho’ chaſt’ned with afflicting rod;
But ſhould my ſins been marked well,
My ſoul would have been ſent to Hell.
10. But bleſſed be his holy Hand,
I am return’d to native land;
Tho’ ſickneſs did my ſpirits waſte,
Thy flowing mercy I do taſte.
11. My Kindred and my Friends ſo dear;
Truſt in the L O R D and always fear
Him who can ſave, if you rely
On One who rules above the ſky.
12. Lo, Seamen, one and all attend,
Look well to what I here have pen’d;
Let me intreat you not to ſwear,
But live in G O D’s moſt holy fear.
13. He will vouchſafe when danger’s
nigh,
To help, if on Him you rely;
Tho’ ſtorms ariſe on raging main,
He’ll bring thee ſafe to Friends again.
14. I ſay to all truſt in the L O R D,
He will your grottos always guard,
From tempeſts dire and ſickneſs ſore;
Your baſket fill, and bleſs your ſtore.
A H Y M N. By another Author.

M Y ſoul, my powers bleſs ye the


Lord,
With Hymns of praiſe his wonders ſound,
When threat’ning danger round me ſtood
I ſought his help and ſuccour found.
2. Hopeleſs I saw my gloomy caſe,
When burſting waves broke o’er my
head,
Then did my ſoul addreſs his throne,
And all its wants before him ſpread.
3. Then in the mount did G O D appear,
And made his light in darkneſs ſhine,
At his rebuke the Tempeſt fled,
To magnify his arm divine.
4. He ſaid I have a ranſom found,
Therefore ye ſons of men return,
I will no more with you contend,
Nor let my wrath forever burn.
5. I am the L O R D who rules the ſeas,
My voice can huſh the roaring waves,
The keys of death are in my Hand
To ſhut or open oozy graves.
6. Come truſt the L O R D in all your
ſtraits,
His great ſalvation call to mind,
My own experience can atteſt,
The L O R D is good, his dealings kind.
7. Within the book of his accounts,
Each of my members written are,
Without commiſſion from his Hand,
There ſhall not fall a limb or hair.
8. With joyful heart I’ll tread his Courts,
And pay my vows among his Saints,
To Him my life, my all devote,
Who bow’d his Ear to my complaints.
Transcriber’s Note
Archaic and inconsistent spelling retained, along with
inconsistent hyphenation (quarter-deck/quarter deck).
To revert to modern letterforms, globally replace "ſ" with "s".
*** END OF THE PROJECT GUTENBERG EBOOK A BRIEF AND
REMARKABLE NARRATIVE OF THE LIFE AND EXTREME
SUFFERINGS OF BARNABAS DOWNS, JUN ***

Updated editions will replace the previous one—the old editions


will be renamed.

Creating the works from print editions not protected by U.S.


copyright law means that no one owns a United States copyright
in these works, so the Foundation (and you!) can copy and
distribute it in the United States without permission and without
paying copyright royalties. Special rules, set forth in the General
Terms of Use part of this license, apply to copying and
distributing Project Gutenberg™ electronic works to protect the
PROJECT GUTENBERG™ concept and trademark. Project
Gutenberg is a registered trademark, and may not be used if
you charge for an eBook, except by following the terms of the
trademark license, including paying royalties for use of the
Project Gutenberg trademark. If you do not charge anything for
copies of this eBook, complying with the trademark license is
very easy. You may use this eBook for nearly any purpose such
as creation of derivative works, reports, performances and
research. Project Gutenberg eBooks may be modified and
printed and given away—you may do practically ANYTHING in
the United States with eBooks not protected by U.S. copyright
law. Redistribution is subject to the trademark license, especially
commercial redistribution.

START: FULL LICENSE


THE FULL PROJECT GUTENBERG LICENSE
PLEASE READ THIS BEFORE YOU DISTRIBUTE OR USE THIS WORK

To protect the Project Gutenberg™ mission of promoting the


free distribution of electronic works, by using or distributing this
work (or any other work associated in any way with the phrase
“Project Gutenberg”), you agree to comply with all the terms of
the Full Project Gutenberg™ License available with this file or
online at www.gutenberg.org/license.

Section 1. General Terms of Use and


Redistributing Project Gutenberg™
electronic works
1.A. By reading or using any part of this Project Gutenberg™
electronic work, you indicate that you have read, understand,
agree to and accept all the terms of this license and intellectual
property (trademark/copyright) agreement. If you do not agree to
abide by all the terms of this agreement, you must cease using
and return or destroy all copies of Project Gutenberg™
electronic works in your possession. If you paid a fee for
obtaining a copy of or access to a Project Gutenberg™
electronic work and you do not agree to be bound by the terms
of this agreement, you may obtain a refund from the person or
entity to whom you paid the fee as set forth in paragraph 1.E.8.

1.B. “Project Gutenberg” is a registered trademark. It may only


be used on or associated in any way with an electronic work by
people who agree to be bound by the terms of this agreement.
There are a few things that you can do with most Project
Gutenberg™ electronic works even without complying with the
full terms of this agreement. See paragraph 1.C below. There
are a lot of things you can do with Project Gutenberg™
electronic works if you follow the terms of this agreement and
help preserve free future access to Project Gutenberg™
electronic works. See paragraph 1.E below.
1.C. The Project Gutenberg Literary Archive Foundation (“the
Foundation” or PGLAF), owns a compilation copyright in the
collection of Project Gutenberg™ electronic works. Nearly all the
individual works in the collection are in the public domain in the
United States. If an individual work is unprotected by copyright
law in the United States and you are located in the United
States, we do not claim a right to prevent you from copying,
distributing, performing, displaying or creating derivative works
based on the work as long as all references to Project
Gutenberg are removed. Of course, we hope that you will
support the Project Gutenberg™ mission of promoting free
access to electronic works by freely sharing Project
Gutenberg™ works in compliance with the terms of this
agreement for keeping the Project Gutenberg™ name
associated with the work. You can easily comply with the terms
of this agreement by keeping this work in the same format with
its attached full Project Gutenberg™ License when you share it
without charge with others.

1.D. The copyright laws of the place where you are located also
govern what you can do with this work. Copyright laws in most
countries are in a constant state of change. If you are outside
the United States, check the laws of your country in addition to
the terms of this agreement before downloading, copying,
displaying, performing, distributing or creating derivative works
based on this work or any other Project Gutenberg™ work. The
Foundation makes no representations concerning the copyright
status of any work in any country other than the United States.

1.E. Unless you have removed all references to Project


Gutenberg:

1.E.1. The following sentence, with active links to, or other


immediate access to, the full Project Gutenberg™ License must
appear prominently whenever any copy of a Project
Gutenberg™ work (any work on which the phrase “Project
Gutenberg” appears, or with which the phrase “Project
Gutenberg” is associated) is accessed, displayed, performed,
viewed, copied or distributed:

This eBook is for the use of anyone anywhere in the United


States and most other parts of the world at no cost and with
almost no restrictions whatsoever. You may copy it, give it
away or re-use it under the terms of the Project Gutenberg
License included with this eBook or online at
www.gutenberg.org. If you are not located in the United
States, you will have to check the laws of the country where
you are located before using this eBook.

1.E.2. If an individual Project Gutenberg™ electronic work is


derived from texts not protected by U.S. copyright law (does not
contain a notice indicating that it is posted with permission of the
copyright holder), the work can be copied and distributed to
anyone in the United States without paying any fees or charges.
If you are redistributing or providing access to a work with the
phrase “Project Gutenberg” associated with or appearing on the
work, you must comply either with the requirements of
paragraphs 1.E.1 through 1.E.7 or obtain permission for the use
of the work and the Project Gutenberg™ trademark as set forth
in paragraphs 1.E.8 or 1.E.9.

1.E.3. If an individual Project Gutenberg™ electronic work is


posted with the permission of the copyright holder, your use and
distribution must comply with both paragraphs 1.E.1 through
1.E.7 and any additional terms imposed by the copyright holder.
Additional terms will be linked to the Project Gutenberg™
License for all works posted with the permission of the copyright
holder found at the beginning of this work.

1.E.4. Do not unlink or detach or remove the full Project


Gutenberg™ License terms from this work, or any files
containing a part of this work or any other work associated with
Project Gutenberg™.
1.E.5. Do not copy, display, perform, distribute or redistribute
this electronic work, or any part of this electronic work, without
prominently displaying the sentence set forth in paragraph 1.E.1
with active links or immediate access to the full terms of the
Project Gutenberg™ License.

1.E.6. You may convert to and distribute this work in any binary,
compressed, marked up, nonproprietary or proprietary form,
including any word processing or hypertext form. However, if
you provide access to or distribute copies of a Project
Gutenberg™ work in a format other than “Plain Vanilla ASCII” or
other format used in the official version posted on the official
Project Gutenberg™ website (www.gutenberg.org), you must, at
no additional cost, fee or expense to the user, provide a copy, a
means of exporting a copy, or a means of obtaining a copy upon
request, of the work in its original “Plain Vanilla ASCII” or other
form. Any alternate format must include the full Project
Gutenberg™ License as specified in paragraph 1.E.1.

1.E.7. Do not charge a fee for access to, viewing, displaying,


performing, copying or distributing any Project Gutenberg™
works unless you comply with paragraph 1.E.8 or 1.E.9.

1.E.8. You may charge a reasonable fee for copies of or


providing access to or distributing Project Gutenberg™
electronic works provided that:

• You pay a royalty fee of 20% of the gross profits you derive from
the use of Project Gutenberg™ works calculated using the
method you already use to calculate your applicable taxes. The
fee is owed to the owner of the Project Gutenberg™ trademark,
but he has agreed to donate royalties under this paragraph to
the Project Gutenberg Literary Archive Foundation. Royalty
payments must be paid within 60 days following each date on
which you prepare (or are legally required to prepare) your
periodic tax returns. Royalty payments should be clearly marked
as such and sent to the Project Gutenberg Literary Archive
Foundation at the address specified in Section 4, “Information
about donations to the Project Gutenberg Literary Archive
Foundation.”

• You provide a full refund of any money paid by a user who


notifies you in writing (or by e-mail) within 30 days of receipt that
s/he does not agree to the terms of the full Project Gutenberg™
License. You must require such a user to return or destroy all
copies of the works possessed in a physical medium and
discontinue all use of and all access to other copies of Project
Gutenberg™ works.

• You provide, in accordance with paragraph 1.F.3, a full refund of


any money paid for a work or a replacement copy, if a defect in
the electronic work is discovered and reported to you within 90
days of receipt of the work.

• You comply with all other terms of this agreement for free
distribution of Project Gutenberg™ works.

1.E.9. If you wish to charge a fee or distribute a Project


Gutenberg™ electronic work or group of works on different
terms than are set forth in this agreement, you must obtain
permission in writing from the Project Gutenberg Literary
Archive Foundation, the manager of the Project Gutenberg™
trademark. Contact the Foundation as set forth in Section 3
below.

1.F.

1.F.1. Project Gutenberg volunteers and employees expend


considerable effort to identify, do copyright research on,
transcribe and proofread works not protected by U.S. copyright
law in creating the Project Gutenberg™ collection. Despite
these efforts, Project Gutenberg™ electronic works, and the
medium on which they may be stored, may contain “Defects,”
such as, but not limited to, incomplete, inaccurate or corrupt
data, transcription errors, a copyright or other intellectual
property infringement, a defective or damaged disk or other
medium, a computer virus, or computer codes that damage or
cannot be read by your equipment.

1.F.2. LIMITED WARRANTY, DISCLAIMER OF DAMAGES -


Except for the “Right of Replacement or Refund” described in
paragraph 1.F.3, the Project Gutenberg Literary Archive
Foundation, the owner of the Project Gutenberg™ trademark,
and any other party distributing a Project Gutenberg™ electronic
work under this agreement, disclaim all liability to you for
damages, costs and expenses, including legal fees. YOU
AGREE THAT YOU HAVE NO REMEDIES FOR NEGLIGENCE,
STRICT LIABILITY, BREACH OF WARRANTY OR BREACH
OF CONTRACT EXCEPT THOSE PROVIDED IN PARAGRAPH
1.F.3. YOU AGREE THAT THE FOUNDATION, THE
TRADEMARK OWNER, AND ANY DISTRIBUTOR UNDER
THIS AGREEMENT WILL NOT BE LIABLE TO YOU FOR
ACTUAL, DIRECT, INDIRECT, CONSEQUENTIAL, PUNITIVE
OR INCIDENTAL DAMAGES EVEN IF YOU GIVE NOTICE OF
THE POSSIBILITY OF SUCH DAMAGE.

1.F.3. LIMITED RIGHT OF REPLACEMENT OR REFUND - If


you discover a defect in this electronic work within 90 days of
receiving it, you can receive a refund of the money (if any) you
paid for it by sending a written explanation to the person you
received the work from. If you received the work on a physical
medium, you must return the medium with your written
explanation. The person or entity that provided you with the
defective work may elect to provide a replacement copy in lieu
of a refund. If you received the work electronically, the person or
entity providing it to you may choose to give you a second
opportunity to receive the work electronically in lieu of a refund.
If the second copy is also defective, you may demand a refund
in writing without further opportunities to fix the problem.

1.F.4. Except for the limited right of replacement or refund set


forth in paragraph 1.F.3, this work is provided to you ‘AS-IS’,
WITH NO OTHER WARRANTIES OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR
ANY PURPOSE.

1.F.5. Some states do not allow disclaimers of certain implied


warranties or the exclusion or limitation of certain types of
damages. If any disclaimer or limitation set forth in this
agreement violates the law of the state applicable to this
agreement, the agreement shall be interpreted to make the
maximum disclaimer or limitation permitted by the applicable
state law. The invalidity or unenforceability of any provision of
this agreement shall not void the remaining provisions.

1.F.6. INDEMNITY - You agree to indemnify and hold the


Foundation, the trademark owner, any agent or employee of the
Foundation, anyone providing copies of Project Gutenberg™
electronic works in accordance with this agreement, and any
volunteers associated with the production, promotion and
distribution of Project Gutenberg™ electronic works, harmless
from all liability, costs and expenses, including legal fees, that
arise directly or indirectly from any of the following which you do
or cause to occur: (a) distribution of this or any Project
Gutenberg™ work, (b) alteration, modification, or additions or
deletions to any Project Gutenberg™ work, and (c) any Defect
you cause.

Section 2. Information about the Mission of


Project Gutenberg™
Project Gutenberg™ is synonymous with the free distribution of
electronic works in formats readable by the widest variety of
computers including obsolete, old, middle-aged and new
computers. It exists because of the efforts of hundreds of
volunteers and donations from people in all walks of life.

Volunteers and financial support to provide volunteers with the


assistance they need are critical to reaching Project
Gutenberg™’s goals and ensuring that the Project Gutenberg™
collection will remain freely available for generations to come. In
2001, the Project Gutenberg Literary Archive Foundation was
created to provide a secure and permanent future for Project
Gutenberg™ and future generations. To learn more about the
Project Gutenberg Literary Archive Foundation and how your
efforts and donations can help, see Sections 3 and 4 and the
Foundation information page at www.gutenberg.org.

Section 3. Information about the Project


Gutenberg Literary Archive Foundation
The Project Gutenberg Literary Archive Foundation is a non-
profit 501(c)(3) educational corporation organized under the
laws of the state of Mississippi and granted tax exempt status by
the Internal Revenue Service. The Foundation’s EIN or federal
tax identification number is 64-6221541. Contributions to the
Project Gutenberg Literary Archive Foundation are tax
deductible to the full extent permitted by U.S. federal laws and
your state’s laws.

The Foundation’s business office is located at 809 North 1500


West, Salt Lake City, UT 84116, (801) 596-1887. Email contact
links and up to date contact information can be found at the
Foundation’s website and official page at
www.gutenberg.org/contact

Section 4. Information about Donations to


the Project Gutenberg Literary Archive
Foundation
Project Gutenberg™ depends upon and cannot survive without
widespread public support and donations to carry out its mission
of increasing the number of public domain and licensed works
that can be freely distributed in machine-readable form
accessible by the widest array of equipment including outdated
equipment. Many small donations ($1 to $5,000) are particularly
important to maintaining tax exempt status with the IRS.

The Foundation is committed to complying with the laws


regulating charities and charitable donations in all 50 states of
the United States. Compliance requirements are not uniform
and it takes a considerable effort, much paperwork and many
fees to meet and keep up with these requirements. We do not
solicit donations in locations where we have not received written
confirmation of compliance. To SEND DONATIONS or
determine the status of compliance for any particular state visit
www.gutenberg.org/donate.

While we cannot and do not solicit contributions from states


where we have not met the solicitation requirements, we know
of no prohibition against accepting unsolicited donations from
donors in such states who approach us with offers to donate.

International donations are gratefully accepted, but we cannot


make any statements concerning tax treatment of donations
received from outside the United States. U.S. laws alone swamp
our small staff.

Please check the Project Gutenberg web pages for current


donation methods and addresses. Donations are accepted in a
number of other ways including checks, online payments and
credit card donations. To donate, please visit:
www.gutenberg.org/donate.

Section 5. General Information About Project


Gutenberg™ electronic works
Professor Michael S. Hart was the originator of the Project
Gutenberg™ concept of a library of electronic works that could
be freely shared with anyone. For forty years, he produced and
distributed Project Gutenberg™ eBooks with only a loose
network of volunteer support.

Project Gutenberg™ eBooks are often created from several


printed editions, all of which are confirmed as not protected by
copyright in the U.S. unless a copyright notice is included. Thus,
we do not necessarily keep eBooks in compliance with any
particular paper edition.

Most people start at our website which has the main PG search
facility: www.gutenberg.org.

This website includes information about Project Gutenberg™,


including how to make donations to the Project Gutenberg
Literary Archive Foundation, how to help produce our new
eBooks, and how to subscribe to our email newsletter to hear
about new eBooks.

You might also like