Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic skeleton with tests #1

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.idea
bin
vendor
composer.lock
55 changes: 55 additions & 0 deletions Features/bootstrap/MainContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

use QATools\BehatExtension\Context\QAToolsContext;

class MainContext extends QAToolsContext
{

/**
* @Given I'm on ':page'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to http://docs.behat.org/en/latest/guides/2.definitions.html the single or double quotes are not required around parameter names. Parameters would be detected in scenario even if somebody adds quotes by accident:

I'm on My Page
I'm on "My Page"
I'm on 'My Page'

Not sure how spaces are handled through.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't check that yet, but good to know, then I will drop them.

*/
public function openPage($page)
{
$this->page = $this->pageFactory->getPage($page);
$this->page->open();
}

/**
* @When I login with ':username' and ':password'
*/
public function login($username, $password)
{
$this->page->login($username, $password);
}

/**
* @Then I should be on ':page'
*/
public function pageOpen($page)
{
$this->page = $this->pageFactory->getPage($page);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any specific reason to assign $this->page also from here (the @Then part)? I thought that only assignment should happen from @Given part.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't seen any best practices yet where to assign what. I just did it here cause I didn't know any good way to detect the successful or failing login in login that why I used @Then to fetch and store the correct page.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't seen any best practices yet where to assign what.

If you'll find them anywhere (e.g. in different programming language/project), then let me know. I also want to know how this should be solved.

I just did it here cause I didn't know any good way to detect the successful or failing login in login that why I used @then to fetch and store the correct page.

My idea was to assign $this->page in @When because that matches to user action (when user opens the page matches logically to page property assignment).


if ( !$this->page->opened() ) {
throw new Exception($page . ' is not open!');
}
}

/**
* @Then I should see ':message' error message
*/
public function errorMessageVisible($message)
{
if ( $this->page->errorPane->getText() !== $message ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess there should be a method in PageObject class to encapsulate error message retrieval (the ->errorPane->getText() part).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, this would be cleaner.

throw new Exception('Error message "' . $message . '" not visible!');
};
}

/**
* @Given /^I wait for ([0-9]+) seconds$/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure, that we can mix different (regexp and non-regxp) step definition ways in same feature context file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I know Gherkin should be backward compatible to regexp. I think I saw it in the slides of the Behat3 presentation. But the new syntax is easier to read.

*/
public function waitFor($seconds)
{
sleep($seconds);
}

}
17 changes: 17 additions & 0 deletions Features/login.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Feature: Login
In order to use the website site
As a website user
I need to login successfully

@javascript @login @failing-login
Scenario: Failing login due wrong password
Given I'm on 'Login Page'
When I login with 'username' and 'wrong password'
Then I should be on 'Login page'
And I should see 'Invalid username or password!' error message

@javascript @login @successful-login
Scenario: Successful login of user
Given I'm on 'Login Page'
When I login with 'username' and 'password'
Then I should be on 'Account Page'
35 changes: 35 additions & 0 deletions Fixtures/account.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<html>
<head>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<!-- Optional theme -->
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
<!-- additional css -->
<link rel="stylesheet" href="additional.css">
<!-- Latest compiled and minified JavaScript -->
<script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
</head>
<body>
<div class="row">
<div class="col-md-5 col-md-offset-1">
<h3>Caption Column 1</h3>
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore
magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd
gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing
elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero
eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum
dolor sit amet.
</div>
<div class="col-md-5">
<h3>Caption Column 2</h3>
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore
magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd
gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing
elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero
eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum
dolor sit amet.
</div>
</div>
</body>
</html>
7 changes: 7 additions & 0 deletions Fixtures/additional.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.margin-top {
margin-top: 20px;
}

.margin-bottom {
margin-bottom: 20px;
}
63 changes: 63 additions & 0 deletions Fixtures/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<html>
<head>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<!-- Optional theme -->
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
<!-- additional css -->
<link rel="stylesheet" href="additional.css">
<!-- Latest compiled and minified JavaScript -->
<script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
</head>
<body>
<div class="row margin-top margin-bottom">
<form id="login-form" role="form" class="col-md-4 col-md-offset-4 panel panel-default" action="login.php" method="POST">
<div class="panel-body">
<?php if ( isset($_GET['error']) ): ?>
<div class="alert alert-danger" role="alert">Invalid username or password!</div>
<?php endif; ?>
<div class="form-group">
<label for="username">Username</label>
<input type="text" class="form-control" name="username" id="username">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" class="form-control" name="password" id="password">
</div>
<input type="submit" class="btn btn-default" value="Login">
</div>
</form>
</div>
<hr>
<div class="row">
<div class="col-md-5 col-md-offset-1">
<h3>Caption Column 1</h3>
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et
dolore
magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd
gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur
sadipscing
elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
vero
eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem
ipsum
dolor sit amet.
</div>
<div class="col-md-5">
<h3>Caption Column 2</h3>
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et
dolore
magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd
gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur
sadipscing
elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
vero
eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem
ipsum
dolor sit amet.
</div>
</div>
</div>
</body>
</html>
26 changes: 26 additions & 0 deletions Fixtures/login.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php
/**
* This file is part of the QA-Tools library.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @copyright Michael Iwersen <[email protected]>
* @link https://github.com/qa-tools/behat-example
*/

if ( empty($_POST) ) {
header('Location: /?error');
die();
}

if ( $_POST['username'] !== 'username' ) {
header('Location: /?error');
die();
}

if ( $_POST['password'] !== 'password' ) {
header('Location: /?error');
die();
}

header('Location: /account.html');
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,25 @@ Install/update your vendors:
$ curl http://getcomposer.org/installer | php
$ php composer.phar install
```

## Configuration and Execution

1. Installing dependencies via composer
2. Setting up a web-server, with PHP support, pointing to **./Fixtures**
3. In case the **Fixture** directory is not reachable via http://localhost change in **behat.yml**
* `base_url` for `MinkExtension`
* `base_url` for `BehatExtension`
```yaml
Behat\MinkExtension:
default_session: selenium2
javascript_session: selenium2
base_url: 'http://localhost'

selenium2: ~
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I always wanted to know what ~ means here. Is it Behat specific or Yaml specific stuff.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needed to search for it http://www.yaml.org/refcard.html, its equals to null or no value.


QATools\BehatExtension:
qa_tools:
base_url: 'http://localhost'
```
4. Download lastest Selenium standalone server and run it
5. Execute `./bin/behat`
18 changes: 18 additions & 0 deletions behat.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
default:
suites:
standard:
paths: ['Features']
contexts: [MainContext]

extensions:
Behat\MinkExtension:
default_session: selenium2
javascript_session: selenium2
base_url: 'http://localhost'

selenium2: ~

QATools\BehatExtension:
qa_tools:
base_url: 'http://localhost'
page_namespace_prefix: ['\QATools\Example\Pages']
24 changes: 24 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "qa-tools/behat-example",
"license": "BSD-3-Clause",
"require": {
"php": ">=5.3.1",
"behat/mink-selenium2-driver": "~1.2.0",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to explicitly specify Mink dependency here as well. Yes I know that it will be indirectly detected through driver dependencies anyway.

"behat/behat": "~3.0",
"behat/mink-extension": "~2.0",
"mindplay/annotations": "~1.2@dev",
"qa-tools/qa-tools": "dev-develop",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please use ~1.1@dev instead? Same goes for behat extension.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry switched to that for testing, yeah I will revert that.

"qa-tools/behat-extension": "dev-develop"
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/evangelion1204/behat-extension.git"
}
],
"autoload-dev": {
"psr-0": {
"QATools\\Example": "./src"
}
}
}
26 changes: 26 additions & 0 deletions src/QATools/Example/Pages/AccountPage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php
/**
* This file is part of the QA-Tools library.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @copyright Michael Iwersen <[email protected]>
* @link https://github.com/qa-tools/behat-example
*/

namespace QATools\Example\Pages;


use QATools\QATools\HtmlElements\Element\Form;
use QATools\QATools\HtmlElements\TypifiedPage;

/**
* Class LoginPage.
*
* @page-url('/account.html')
* @url-match-full('/account.html')
*/
class AccountPage extends TypifiedPage
{

}
65 changes: 65 additions & 0 deletions src/QATools/Example/Pages/LoginPage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php
/**
* This file is part of the QA-Tools library.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @copyright Michael Iwersen <[email protected]>
* @link https://github.com/qa-tools/behat-example
*/

namespace QATools\Example\Pages;


use QATools\QATools\HtmlElements\Element\Form;
use QATools\QATools\HtmlElements\Element\TextBlock;
use QATools\QATools\HtmlElements\TypifiedPage;

/**
* Class LoginPage.
*
* @page-url('/')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Home home, that LoginPage is what's shown by default?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, its just a simple example for now.

* @url-match-full('/')
*/
class LoginPage extends TypifiedPage
{

/**
* The login form.
*
* @var Form
* @find-by('id' => 'login-form')
*/
public $loginForm;

/**
* The error label.
*
* @var TextBlock
* @find-by('css' => '.alert')
*/
public $errorPane;

/**
* Does the login.
*
* @param string $username The username.
* @param string $password The password.
* @param string $success The success page.
* @param string $failure The failure page.
*
* @return static
*/
public function login($username, $password)
{
$this->loginForm->fill(array(
'username' => $username,
'password' => $password,
));

$this->loginForm->submit();

return $this;
}

}