You can file this one under the ‘practice what you preach’ category. While getting PHP-WebDriver ready for the OnDemand stuff that was announced earlier in the week I was scripting up the Sauce Labs login page I realized that I was writing things in the poor, hard-to-maintain, non-page-object manner.
Ugh-oh.
So I started to use a Page Object for it. Here is the link directly to it. Although I’m annotating it here.
array(\PHPWebDriver_WebDriverBy::ID, 'username'),
"password" => array(\PHPWebDriver_WebDriverBy::ID, 'password'),
"submit button" => array(\PHPWebDriver_WebDriverBy::ID, 'submit'),
"errors" => array(\PHPWebDriver_WebDriverBy::CSS_SELECTOR, '.error')
);
// dependency injection
function __construct($session) {
$this->session = $session;
}
// interactions where you pull something out of the browser get handled
// by manipulating the __get's
function __get($property) {
switch($property) {
case "errors":
list($type, $string) = $this->locators[$property];
$e = $this->session->element($type, $string);
return $e->text();
case "title":
return $this->session->title();
default:
return $this->$property;
}
}
// surprising, when you want to put something into the browser, you
// manipulate __set
function __set($property, $value) {
switch($property) {
case "username":
case "password":
list($type, $string) = $this->locators[$property];
$e = $this->session->element($type, $string);
$e->sendKeys($value);
break;
default:
$this->$property = $value;
}
}
// if you can get to a page directly, then open does something
// if not, then it can either traverse through your app to get to it
// or just 'return $this;'
function open() {
$this->session->open("https://saucelabs.com/login");
return $this;
}
// synchronization for what 'done' means on this page. 'done' rarely
// means 'page content loaded' anymore
function wait_until_loaded() {
$w = new \PHPWebDriver_WebDriverWait($this->session, 30, 0.5, array("locator" => $this->locators['submit button']));
$w->until(
function($session, $extra_arguments) {
list($type, $string) = $extra_arguments['locator'];
return $session->element($type, $string);
}
);
return $this;
}
// still not sure how i feel about this...
// not asserts to be run every time, maybe once per suite
// certainly no functionality checks though
function validate() {
assert('$this->title == "Login - Sauce Labs" /* title should be "Login - Sauce Labs" */');
return $this;
}
// here is an 'action', including a default route (success)
// notice how there is a page object instance returned in both routes
function login_as($username, $password, $success=true) {
$this->username = $username;
$this->password = $password;
list($type, $string) = $this->locators['submit button'];
$e = $this->session->element($type, $string);
$e->click();
if ($success) {
$p = new \DashboardPage($this->session);
$p->wait_until_loaded();
return $p;
} else {
$w = new \PHPWebDriver_WebDriverWait($this->session, 30, 0.5, array("locator" => $this->locators['errors']));
$w->until(
function($session, $extra_arguments) {
list($type, $string) = $extra_arguments['locator'];
$e = $session->element($type, $string);
return $e->displayed();
}
);
return $this;
}
}
}
Of course, when you are using page objects, you scripts look different. As in there is no WebDriver-isms.
public function testFirefox36() {
$caps = array();
$caps["platform"] = 'LINUX';
$caps["version"] = '3.6';
$this->session = self::$driver->session("firefox", $caps);
$p = new SauceLoginPage($this->session);
$p->open();
$p->wait_until_loaded();
$p->validate();
$p = $p->login_as("gobblygook", "nonsense", false);
$this->assertEquals($p->errors, "Incorrect username or password.");
}
One thing I need to really get into the habit of doing is including more Page Objects in my code examples. I’m looking forward to the next version of Selenium 2 Testing Tools: Beginner’s Guide as he mentioned he’ll be using more Page Objects in his examples.
Comments 1
I’m curious about why some tests are part of the PageObject and some aren’t. For instance, the validate() method asserts that the title is correct, but then you make an assert for incorrect login details in the main test. Shouldn’t this be part of the login_as() method?
Posted 10 Sep 2012 at 6:25 am ¶Trackbacks & Pingbacks 1
[…] I finally annotated A PHP Page Object Example […]
Post a Comment