Commit d7485f0f authored by Son Nguyen's avatar Son Nguyen Committed by Gerrit Code Review

Integrate behat

Change-Id: I7a1f7d42d9739a9ab1507d56aaf3a51ee0e7ef1a
parent 2dbcef7d
<?php
/**
* @package mahara
* @subpackage test/behat
* @author Son Nguyen, Catalyst IT Ltd
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later
* @copyright For copyright information on Mahara, please see the README file distributed with this software.
* @copyright portions from mahara Behat, 2013 David Monllaó
*
*/
require_once(dirname(dirname(dirname(__DIR__))) . '/testing/frameworks/behat/classes/BehatBase.php');
use Behat\Behat\Context\Step\Given as Given,
Behat\Gherkin\Node\TableNode as TableNode,
Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException;
/**
* Account steps definitions.
*
*/
class BehatAccount extends BehatBase {
/**
* Sets the specified account settings. A table with | Setting label | value | is expected.
*
* @Given /^the following account settings have set:$/
* @param TableNode $table
*/
public function i_set_the_following_account_settings_values(TableNode $table) {
// @TODO implement this step definition
}
}
\ No newline at end of file
@javascript @core @account
Feature: Mahara users can change their account settings
As a mahara user
I need to change my account settings
Scenario: Change password
Given the following "users" exist:
| user | password | institution | role |
| userA | Password1 | mahara | member |
And I log in as "userA" with password "Password1"
And I follow "Settings"
And I fill in "oldpassword" with "Password1"
And I fill in "password1" with "Passwordnew"
And I fill in "password2" with "Passwordnew"
And I press "Save"
Then I should see "Preferences saved"
Scenario: Change notifications
Given the following "users" exist:
| user | password | institution | role |
| userA | Password1 | mahara | member |
And I log in as "userA" with password "Password1"
And I follow "Settings"
And I follow "Notifications"
And I select "Email" from "activity_viewaccess"
And I press "Save"
Then I should see "Preferences saved"
\ No newline at end of file
......@@ -37,6 +37,12 @@ $options['adminemail']->shortoptions = array('e');
$options['adminemail']->description = get_string('cliadminemail', 'admin');
$options['adminemail']->required = true;
$options['sitename'] = new stdClass();
$options['sitename']->examplevalue = 'Mahara site';
$options['sitename']->shortoptions = array('n');
$options['sitename']->description = get_string('clisitename', 'admin');
$options['sitename']->required = false;
$settings = new stdClass();
$settings->options = $options;
$settings->info = get_string('cliinstallerdescription', 'admin');
......@@ -77,3 +83,10 @@ $userobj->commit();
// Password changes should be performed by the authfactory
$authobj = AuthFactory::create($userobj->authinstance);
$authobj->change_password($userobj, $adminpassword, true);
// Set site name
if ($sitename = $cli->get_cli_param('sitename')) {
if (!set_config('sitename', $sitename)) {
cli::cli_exit(get_string('cliupdatesitenamefailed', 'admin'), true);
}
}
@javasript @plugin @artefact.blog
Feature: Mahara users can create their blogs
As a mahara user
I need to create blogs
Scenario: create blogs
Given the following "users" exist:
| user | password | | institution | role |
| userA | Password1 | | mahara | member |
And the following account settings have set:
| Multiple journals | ON |
When I log in as "userA" with password "Password1"
And I follow "Content"
And I follow "Journal"
Then I should see "Journals"
When I press "Create journal"
And I fill in the following:
| title | My new journal |
| description | <p>This is my new journal</p> |
| tags | blog |
And I press "Create journal"
Then I should see "My new journal"
\ No newline at end of file
{
"require": {
"php": ">=5.3.2",
"behat/behat": "2.5.1",
"behat/mink": "1.5.0",
"behat/mink-extension": "*",
"behat/mink-goutte-driver": "*",
"behat/mink-selenium-driver": "*",
"behat/mink-selenium2-driver": "*"
},
"minimum-stability": "dev"
}
......@@ -16,7 +16,7 @@ if (defined('CLI') && php_sapi_name() != 'cli') {
}
$CFG = new StdClass;
$CFG->docroot = dirname(__FILE__) . '/';
$CFG->docroot = dirname(__FILE__) . DIRECTORY_SEPARATOR;
//array containing site options from database that are overrided by $CFG
$OVERRIDDEN = array();
......@@ -25,18 +25,10 @@ if (!empty($_SERVER['MAHARA_LIBDIR'])) {
$CFG->libroot = $_SERVER['MAHARA_LIBDIR'];
}
else {
$CFG->libroot = dirname(__FILE__) . '/lib/';
$CFG->libroot = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR;
}
set_include_path($CFG->libroot . PATH_SEPARATOR . $CFG->libroot . 'pear/' . PATH_SEPARATOR . get_include_path());
// Ensure that, by default, the response is not cached
header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0');
header('Expires: '. gmdate('D, d M Y H:i:s', 507686400) .' GMT');
header('Pragma: no-cache');
// Prevent clickjacking through iframe tags
header('X-Frame-Options: SAMEORIGIN');
// Set up error handling
require('errors.php');
......@@ -75,16 +67,6 @@ $CFG = (object)array_merge((array)$cfg, (array)$CFG);
require_once('config-defaults.php');
$CFG = (object)array_merge((array)$cfg, (array)$CFG);
// Fix up paths in $CFG
foreach (array('docroot', 'dataroot') as $path) {
$CFG->{$path} = (substr($CFG->{$path}, -1) != '/') ? $CFG->{$path} . '/' : $CFG->{$path};
}
// Set default configs that are dependent on the docroot and dataroot
if (empty($CFG->sessionpath)) {
$CFG->sessionpath = $CFG->dataroot . 'sessions';
}
// xmldb stuff
$CFG->xmldbdisablenextprevchecking = true;
$CFG->xmldbdisablecommentchecking = true;
......@@ -95,12 +77,82 @@ if (empty($CFG->directorypermissions)) {
}
$CFG->filepermissions = $CFG->directorypermissions & 0666;
if (defined('BEHAT_SITE_RUNNING')) {
// We already switched to behat test site previously.
}
else if (!empty($CFG->behat_wwwroot) ||empty($CFG->behat_dataroot) || !empty($CFG->behat_dbprefix)) {
// The behat is configured on this server, we need to find out if this is the behat test
// site based on the URL used for access.
require_once($CFG->docroot . '/testing/frameworks/behat/lib.php');
if (behat_is_test_site()) {
// Checking the integrity of the provided $CFG->behat_* vars and the
// selected wwwroot to prevent conflicts with production and phpunit environments.
behat_check_config_vars();
// Check that the directory does not contains other things.
if (!file_exists("$CFG->behat_dataroot/behattestdir.txt")) {
if ($dh = opendir($CFG->behat_dataroot)) {
while (($file = readdir($dh)) !== false) {
if ($file === 'behat' || $file === '.' || $file === '..' || $file === '.DS_Store') {
continue;
}
behat_error(BEHAT_EXITCODE_CONFIG, '$CFG->behat_dataroot directory is not empty, ensure this is the directory where you want to install behat test dataroot');
}
closedir($dh);
unset($dh);
unset($file);
}
if (defined('BEHAT_UTIL')) {
// Now we create dataroot directory structure for behat tests.
testing_initdataroot($CFG->behat_dataroot, 'behat');
} else {
behat_error(BEHAT_EXITCODE_INSTALL);
}
}
if (!defined('BEHAT_UTIL') && !defined('BEHAT_TEST')) {
// Somebody tries to access test site directly, tell them if not enabled.
if (!file_exists($CFG->behat_dataroot . '/behat/test_environment_enabled.txt')) {
behat_error(BEHAT_EXITCODE_CONFIG, 'Behat is configured but not enabled on this test site.');
}
}
// Constant used to inform that the behat test site is being used,
// this includes all the processes executed by the behat CLI command like
// the site reset, the steps executed by the browser drivers when simulating
// a user session and a real session when browsing manually to $CFG->behat_wwwroot
// like the browser driver does automatically.
// Different from BEHAT_TEST as only this last one can perform CLI
// actions like reset the site or use data generators.
define('BEHAT_SITE_RUNNING', true);
// Clean extra config.php settings.
//behat_clean_init_config();
// Now we can begin switching $CFG->X for $CFG->behat_X.
$CFG->wwwroot = $CFG->behat_wwwroot;
$CFG->dbprefix = $CFG->behat_dbprefix;
$CFG->dataroot = $CFG->behat_dataroot;
}
}
// Fix up paths in $CFG
foreach (array('docroot', 'dataroot') as $path) {
$CFG->{$path} = (substr($CFG->{$path}, -1) != DIRECTORY_SEPARATOR) ? $CFG->{$path} . DIRECTORY_SEPARATOR : $CFG->{$path};
}
// Set default configs that are dependent on the docroot and dataroot
if (empty($CFG->sessionpath)) {
$CFG->sessionpath = $CFG->dataroot . 'sessions';
}
// Now that we've loaded the configs, we can override the default error settings
// from errors.php
$errorlevel = $CFG->error_reporting;
error_reporting($errorlevel);
set_error_handler('error', $errorlevel);
// core libraries
require('mahara.php');
ensure_sanity();
......@@ -321,7 +373,16 @@ if (!get_config('productionmode')) {
$CFG->nocache = true;
}
header('Content-type: text/html; charset=UTF-8');
if (!defined('CLI')) {
header('Content-type: text/html; charset=UTF-8');
// Ensure that, by default, the response is not cached
header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0');
header('Expires: '. gmdate('D, d M Y H:i:s', 507686400) .' GMT');
header('Pragma: no-cache');
// Prevent clickjacking through iframe tags
header('X-Frame-Options: SAMEORIGIN');
}
// Only do authentication once we know the page theme, so that the login form
// can have the correct theming.
......@@ -374,7 +435,8 @@ if (!get_config('installed')) {
ensure_install_sanity();
$scriptfilename = str_replace('\\', '/', $_SERVER['SCRIPT_FILENAME']);
if (false === strpos($scriptfilename, 'admin/index.php')
if (!defined('CLI')
&& false === strpos($scriptfilename, 'admin/index.php')
&& false === strpos($scriptfilename, 'admin/upgrade.php')
&& false === strpos($scriptfilename, 'admin/upgrade.json.php')
&& false === strpos($scriptfilename, 'admin/cli/install.php')
......
......@@ -863,3 +863,11 @@ jQuery(document).ready(function() {
jQuery(document).ready(function() {
jQuery('body').removeClass('no-js').addClass('js');
});
/**
* Check if the page is ready in javascript
*/
var is_page_ready = false;
jQuery(document).ready(function() {
is_page_ready = true;
});
......@@ -55,6 +55,8 @@ $string['dbcollationmismatch'] = 'A column of your database is using a collation
$string['maharainstalled'] = 'Mahara is already installed.';
$string['cliadminpassword'] = 'The password for the admin user';
$string['cliadminemail'] = 'The email address for the admin user';
$string['clisitename'] = 'The site name';
$string['cliupdatesitenamefailed'] = 'Updating site name failed.';
$string['cliinstallerdescription'] = 'Install Mahara and create required data directories';
$string['cliinstallingmahara'] = 'Installing Mahara';
$string['cliupgraderdescription'] = 'Upgrade the Mahara database and data to the version of Mahara installed';
......
<?php
/**
*
* @package mahara
* @subpackage lang/behat
* @author Catalyst IT Ltd
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later
* @copyright For copyright information on Mahara, please see the README file distributed with this software.
*
*/
defined('INTERNAL') || die();
// lang strings for behat
$string['errorbehatcommand'] = 'Error running behat CLI command. Try running "{$a} --help" manually from CLI to find out more about the problem.';
$string['errorcomposer'] = 'Composer dependencies are not installed.';
$string['errordataroot'] = '$CFG->behat_dataroot is not set or is invalid.';
$string['errorsetconfig'] = '$CFG->behat_dataroot, $CFG->behat_dbprefix and $CFG->behat_wwwroot need to be set in config.php.';
$string['erroruniqueconfig'] = '$CFG->behat_dataroot, $CFG->behat_dbprefix and $CFG->behat_wwwroot values need to be different than $CFG->dataroot, $CFG->dbprefix, $CFG->wwwroot, $CFG->phpunit_dataroot and $CFG->phpunit_prefix values.';
......@@ -1411,3 +1411,74 @@ function rename_index($table, $index, $newname, $continue=true, $feedback=true)
return execute_sql_arr($sqlarr, $continue, $feedback);
}
/**
* Return all tables in current db
*
* @return array('tablename' => 'tablename', ...)
* Note all table names is in lower cases
*/
function get_tables() {
global $CFG, $db;
// Get all tables in current DB
$tables = $metatables = $db->MetaTables('TABLES');
if (!empty($CFG->prefix)) {
$tables = array();
foreach ($metatables as $mtable) {
if (strpos($mtable, $CFG->prefix) !== false) {
$tables[] = $mtable;
}
}
}
unset($metatables);
$tnames = array();
foreach ($tables as $t) {
$t = strtolower($t);
$tnames[$t] = $t;
}
return $tnames;
}
/**
* Return all columns of a table in current db
*
* @param string $tablename should be a full name including the dbprefix
* @return array of ADOFieldObject
*/
function get_columns($tablename) {
global $CFG, $db;
$columns = $db->MetaColumns($tablename);
// Update the field Auto_increment if postgres
// Only apply for "id" field
if (is_postgres()) {
if (isset($columns['id'])) {
$idcolumn = $columns['id'];
if (isset($idcolumn->primary_key) && ($idcolumn->primary_key === 1)
&& isset($idcolumn->default_value)
&& strpos($idcolumn->default_value, 'nextval(') !== false ) {
$rec = get_record_sql('SELECT last_value FROM '. "{$tablename}" . '_id_seq');
$idcolumn->Auto_increment = $rec->last_value + 1;
}
$columns['id'] = $idcolumn;
}
}
return $columns;
}
/**
* Return the server info
*
* @return an array of containing two elements 'description' and 'version'
*/
function get_server_info() {
global $CFG, $db;
return $db->ServerInfo();
}
......@@ -876,3 +876,51 @@ function file_cleanup_old_cached_files() {
}
}
}
/**
* Create a directory and make sure it is writable.
*
* @private
* @param string $dir the full path of the directory to be created
* @param bool $exceptiononerror throw exception if error encountered
* @return string|false Returns full path to directory if successful, false if not; may throw exception
*/
function make_writable_directory($dir, $exceptiononerror = true) {
global $CFG;
if (file_exists($dir) && !is_dir($dir)) {
if ($exceptiononerror) {
throw new SystemException($dir . ' directory can not be created, file with the same name already exists.');
}
else {
return false;
}
}
if (!file_exists($dir)) {
if (!mkdir($dir, $CFG->directorypermissions, true)) {
clearstatcache();
// There might be a race condition when creating directory.
if (!is_dir($dir)) {
if ($exceptiononerror) {
throw new SystemException($dir . ' can not be created, check permissions.');
}
else {
debugging('Can not create directory: ' . $dir, DEBUG_DEVELOPER);
return false;
}
}
}
}
if (!is_writable($dir)) {
if ($exceptiononerror) {
throw new SystemException($dir . ' is not writable, check permissions.');
}
else {
return false;
}
}
return $dir;
}
......@@ -1594,6 +1594,11 @@ function generate_interaction_instance_class_name($type) {
return 'Interaction' . ucfirst($type) . 'Instance';
}
function generate_generator_class_name() {
$args = func_get_args();
return 'DataGenerator' . implode('', array_map('ucfirst', $args));
}
function blocktype_namespaced_to_single($blocktype) {
if (strpos($blocktype, '/') === false) { // system blocktype
return $blocktype;
......@@ -4012,3 +4017,96 @@ function get_user_institution_comment_sort_order($userid = null) {
}
return $sortorder;
}
/**
* Returns all directories of installed plugins except for local
* from the current codebase.
*
* This is relatively slow and not fully cached, use with care!
*
* @return array ('plugintkey' => path, ...)
* For example, array (
* 'artefact.blog' => $CFG->docroot . 'artefact/blog',
* 'blocktype.blog' => $CFG->docroot . 'artefact/blog/blocktype/blog',
* ...
* )
*/
function get_installed_plugins_paths() {
$versions = array();
// All installed plugins
$plugins = array();
foreach (plugin_types_installed() as $plugin) {
$dirhandle = opendir(get_config('docroot') . $plugin);
while (false !== ($dir = readdir($dirhandle))) {
if (strpos($dir, '.') === 0 || 'CVS' == $dir) {
continue;
}
if (!is_dir(get_config('docroot') . $plugin . '/' . $dir)) {
continue;
}
try {
validate_plugin($plugin, $dir);
$plugins[] = array($plugin, $dir);
}
catch (InstallationException $_e) {
log_warn("Plugin $plugin $dir is not installable: " . $_e->GetMessage());
}
if ($plugin === 'artefact') { // go check it for blocks as well
$btlocation = get_config('docroot') . $plugin . '/' . $dir . '/blocktype';
if (!is_dir($btlocation)) {
continue;
}
$btdirhandle = opendir($btlocation);
while (false !== ($btdir = readdir($btdirhandle))) {
if (strpos($btdir, '.') === 0 || 'CVS' == $btdir) {
continue;
}
if (!is_dir(get_config('docroot') . $plugin . '/' . $dir . '/blocktype/' . $btdir)) {
continue;
}
$plugins[] = array('blocktype', $dir . '/' . $btdir);
}
}
}
}
$pluginpaths = array();
foreach ($plugins as $plugin) {
$plugintype = $plugin[0];
$pluginname = $plugin[1];
$pluginpath = "$plugin[0]/$plugin[1]";
$pluginkey = "$plugin[0].$plugin[1]";
if ($plugintype == 'blocktype' && strpos($pluginname, '/') !== false) {
$bits = explode('/', $pluginname);
$pluginpath = 'artefact/' . $bits[0] . '/blocktype/' . $bits[1];
}
$pluginpaths[$pluginkey] = get_config('docroot') . $pluginpath;
}
return $pluginpaths;
}
/**
* Returns hash of all versions including core and all installed plugins except for local
* from the current codebase.
*
* This is relatively slow and not fully cached, use with care!
*
* @return string sha1 hash
*/
function get_all_versions_hash() {
$versions = array();
// Get core version
require(get_config('libroot') . 'version.php');
$versions['core'] = $config->version;
// All installed plugins
$pluginpaths = get_installed_plugins_paths();
foreach ($pluginpaths as $pluginkey => $pluginpath) {
require($pluginpath . '/version.php');
$versions[$pluginkey] = $config->version;
}
return sha1(serialize($versions));
}
<?php
/**
* @package mahara
* @subpackage test/core
* @author David Monllaó 2013; Son Nguyen, Catalyst IT Ltd
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later
* @copyright For copyright information on Mahara, please see the README file distributed with this software.
* @copyright portions from Moodle 2013 David Monllaó
/**
* Nasty strings to use in tests.
*
*/
defined('INTERNAL') || die;
/**
* Nasty strings manager.
*
* Responds to nasty strings requests with a random string of the list
* to try with different combinations in different places.
*
*/
class NastyStrings {
/**
* List of different strings to fill fields and assert against them
*
* Non of these strings can be a part of another one, this would not be good
* when using more one string at the same time and asserting results.
*
* @static
* @var array
*/
protected static $strings = array(
'< > & &lt; &gt; &amp; \' \\" \ \'$@NULL@$ @@TEST@@ \\\" \\ , ; : . 日本語­% %%',
'&amp; \' \\" \ \'$@NULL@$ < > & &lt; &gt; @@TEST@@ \\\" \\ , ; : . 日本語­% %%',
'< > & &lt; &gt; &amp; \' \\" \ \\\" \\ , ; : . \'$@NULL@$ @@TEST@@ 日本語­% %%',
'< > & &lt; &gt; &amp; \' \\" \ \'$@NULL@$ 日本語­% %%@@TEST@@ \. \\" \\ , ; :',
'< > & &lt; &gt; \\\" \\ , ; : . 日本語&amp; \' \\" \ \'$@NULL@$ @@TEST@@­% %%',
'\' \\" \ \'$@NULL@$ @@TEST@@ < > & &lt; &gt; &amp; \\\" \\ , ; : . 日本語­% %%',
'\\\" \\ , ; : . 日本語­% < > & &lt; &gt; &amp; \' \\" \ \'$@NULL@$ @@TEST@@ %%',
'< > & &lt; &gt; &amp; \' \\" \ \'$@NULL@$ 日本語­% %% @@TEST@@ \\\" \\ . , ; :',
'. 日本語&amp; \' \\" < > & &lt; &gt; \\ , ; : \ \'$@NULL@$ \\\" @@TEST@@­% %%',
'&amp; \' \\" \ < > & &lt; &gt; \\\" \\ , ; : . 日本語\'$@NULL@$ @@TEST@@­% %%',
);
/**
* Already used nasty strings.
*
* This array will be cleaned before each scenario.
*
* @static
* @var array
*/
protected static $usedstrings = array();
/**
* Returns a nasty string and stores the key mapping.
*
* @static
* @param string $key The key
* @return string
*/
public static function get($key) {
// If have been used during the this tests return it.
if (isset(self::$usedstrings[$key])) {
return self::$strings[self::$usedstrings[$key]];
}