diff options
Diffstat (limited to 'tests/test_framework/phpbb_functional_test_case.php')
-rw-r--r-- | tests/test_framework/phpbb_functional_test_case.php | 697 |
1 files changed, 600 insertions, 97 deletions
diff --git a/tests/test_framework/phpbb_functional_test_case.php b/tests/test_framework/phpbb_functional_test_case.php index 9b64600c7d..8107e45dc7 100644 --- a/tests/test_framework/phpbb_functional_test_case.php +++ b/tests/test_framework/phpbb_functional_test_case.php @@ -1,16 +1,18 @@ <?php /** * -* @package testing -* @copyright (c) 2011 phpBB Group -* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. * */ use Symfony\Component\BrowserKit\CookieJar; require_once __DIR__ . '/../../phpBB/includes/functions_install.php'; -require_once __DIR__ . '/../../phpBB/includes/acm/acm_file.php'; -require_once __DIR__ . '/../../phpBB/includes/cache.php'; class phpbb_functional_test_case extends phpbb_test_case { @@ -20,6 +22,7 @@ class phpbb_functional_test_case extends phpbb_test_case protected $cache = null; protected $db = null; + protected $extension_manager = null; /** * Session ID for current test's session (each test makes its own) @@ -35,6 +38,7 @@ class phpbb_functional_test_case extends phpbb_test_case static protected $config = array(); static protected $already_installed = false; + static protected $last_post_timestamp = 0; static public function setUpBeforeClass() { @@ -43,6 +47,12 @@ class phpbb_functional_test_case extends phpbb_test_case self::$config = phpbb_test_case_helpers::get_test_config(); self::$root_url = self::$config['phpbb_functional_url']; + // Important: this is used both for installation and by + // test cases for querying the tables. + // Therefore table prefix must be set before a board is + // installed, and also before each test case is run. + self::$config['table_prefix'] = 'phpbb_'; + if (!isset(self::$config['phpbb_functional_url'])) { self::markTestSkipped('phpbb_functional_url was not set in test_config and wasn\'t set as PHPBB_FUNCTIONAL_URL environment variable either.'); @@ -55,6 +65,14 @@ class phpbb_functional_test_case extends phpbb_test_case } } + /** + * @return array List of extensions that should be set up + */ + static protected function setup_extensions() + { + return array(); + } + public function setUp() { parent::setUp(); @@ -71,7 +89,36 @@ class phpbb_functional_test_case extends phpbb_test_case // that were added in other tests are gone $this->lang = array(); $this->add_lang('common'); - $this->purge_cache(); + + $db = $this->get_db(); + + foreach (static::setup_extensions() as $extension) + { + $this->purge_cache(); + + $sql = 'SELECT ext_active + FROM ' . EXT_TABLE . " + WHERE ext_name = '" . $db->sql_escape($extension). "'"; + $result = $db->sql_query($sql); + $status = (bool) $db->sql_fetchfield('ext_active'); + $db->sql_freeresult($result); + + if (!$status) + { + $this->install_ext($extension); + } + } + } + + protected function tearDown() + { + parent::tearDown(); + + if ($this->db instanceof \phpbb\db\driver\driver_interface) + { + // Close the database connections again this test + $this->db->sql_close(); + } } /** @@ -132,18 +179,23 @@ class phpbb_functional_test_case extends phpbb_test_case { } + public function __construct($name = NULL, array $data = array(), $dataName = '') + { + parent::__construct($name, $data, $dataName); + + $this->backupStaticAttributesBlacklist += array( + 'phpbb_functional_test_case' => array('config', 'already_installed'), + ); + } + protected function get_db() { global $phpbb_root_path, $phpEx; // so we don't reopen an open connection - if (!($this->db instanceof dbal)) + if (!($this->db instanceof \phpbb\db\driver\driver_interface)) { - if (!class_exists('dbal_' . self::$config['dbms'])) - { - include($phpbb_root_path . 'includes/db/' . self::$config['dbms'] . ".$phpEx"); - } - $sql_db = 'dbal_' . self::$config['dbms']; - $this->db = new $sql_db(); + $dbms = self::$config['dbms']; + $this->db = new $dbms(); $this->db->sql_connect(self::$config['dbhost'], self::$config['dbuser'], self::$config['dbpasswd'], self::$config['dbname'], self::$config['dbport']); } return $this->db; @@ -153,7 +205,7 @@ class phpbb_functional_test_case extends phpbb_test_case { if (!$this->cache) { - $this->cache = new cache(); + $this->cache = new \phpbb\cache\driver\file; } return $this->cache; @@ -168,20 +220,50 @@ class phpbb_functional_test_case extends phpbb_test_case $cache->load(); } - public function __construct($name = NULL, array $data = array(), $dataName = '') + protected function get_extension_manager() { - parent::__construct($name, $data, $dataName); + global $phpbb_root_path, $phpEx; - $this->backupStaticAttributesBlacklist += array( - 'phpbb_functional_test_case' => array('config', 'already_installed'), + $config = new \phpbb\config\config(array()); + $db = $this->get_db(); + $db_tools = new \phpbb\db\tools($db); + + $container = new phpbb_mock_container_builder(); + $migrator = new \phpbb\db\migrator( + $container, + $config, + $db, + $db_tools, + self::$config['table_prefix'] . 'migrations', + $phpbb_root_path, + $phpEx, + self::$config['table_prefix'], + array(), + new \phpbb\db\migration\helper() ); + $container->set('migrator', $migrator); + $container->set('dispatcher', new phpbb_mock_event_dispatcher()); + $user = new \phpbb\user('\phpbb\datetime'); + + $extension_manager = new \phpbb\extension\manager( + $container, + $db, + $config, + new phpbb\filesystem(), + $user, + self::$config['table_prefix'] . 'ext', + dirname(__FILE__) . '/', + $phpEx, + $this->get_cache_driver() + ); + + return $extension_manager; } static protected function install_board() { global $phpbb_root_path, $phpEx; - self::$config['table_prefix'] = 'phpbb_'; self::recreate_database(self::$config); $config_file = $phpbb_root_path . "config.$phpEx"; @@ -216,7 +298,7 @@ class phpbb_functional_test_case extends phpbb_test_case $parseURL = parse_url(self::$config['phpbb_functional_url']); - $crawler = self::request('GET', 'install/index.php?mode=install'); + $crawler = self::request('GET', 'install/index.php?mode=install&language=en'); self::assertContains('Welcome to Installation', $crawler->filter('#main')->text()); $form = $crawler->selectButton('submit')->form(); @@ -230,7 +312,7 @@ class phpbb_functional_test_case extends phpbb_test_case self::assertContains('Database configuration', $crawler->filter('#main')->text()); $form = $crawler->selectButton('submit')->form(array( // Installer uses 3.0-style dbms name - 'dbms' => str_replace('phpbb_db_driver_', '', self::$config['dbms']), + 'dbms' => str_replace('phpbb\db\driver\\', '', self::$config['dbms']), 'dbhost' => self::$config['dbhost'], 'dbport' => self::$config['dbport'], 'dbname' => self::$config['dbname'], @@ -252,8 +334,7 @@ class phpbb_functional_test_case extends phpbb_test_case 'admin_name' => 'admin', 'admin_pass1' => 'adminadmin', 'admin_pass2' => 'adminadmin', - 'board_email1' => 'nobody@example.com', - 'board_email2' => 'nobody@example.com', + 'board_email' => 'nobody@example.com', )); // install/index.php?mode=install&sub=administrator @@ -265,7 +346,7 @@ class phpbb_functional_test_case extends phpbb_test_case // because that step will create a config.php file if phpBB has the // permission to do so. We have to create the config file on our own // in order to get the DEBUG constants defined. - $config_php_data = phpbb_create_config_file_data(self::$config, self::$config['dbms'], array(), true, true); + $config_php_data = phpbb_create_config_file_data(self::$config, self::$config['dbms'], true, false, true); $config_created = file_put_contents($config_file, $config_php_data) !== false; if (!$config_created) { @@ -317,6 +398,36 @@ class phpbb_functional_test_case extends phpbb_test_case copy($config_file, $config_file_test); } + public function install_ext($extension) + { + $this->login(); + $this->admin_login(); + + $ext_path = str_replace('/', '%2F', $extension); + + $crawler = self::request('GET', 'adm/index.php?i=acp_extensions&mode=main&action=enable_pre&ext_name=' . $ext_path . '&sid=' . $this->sid); + $this->assertGreaterThan(0, $crawler->filter('.submit-buttons')->count()); + + $form = $crawler->selectButton('Enable')->form(); + $crawler = self::submit($form); + $this->add_lang('acp/extensions'); + + $meta_refresh = $crawler->filter('meta[http-equiv="refresh"]'); + + // Wait for extension to be fully enabled + while (sizeof($meta_refresh)) + { + preg_match('#url=.+/(adm+.+)#', $meta_refresh->attr('content'), $match); + $url = $match[1]; + $crawler = self::request('POST', $url); + $meta_refresh = $crawler->filter('meta[http-equiv="refresh"]'); + } + + $this->assertContainsLang('EXTENSION_ENABLE_SUCCESS', $crawler->filter('div.successbox')->text()); + + $this->logout(); + } + static private function recreate_database($config) { $db_conn_mgr = new phpbb_database_test_connection_manager($config); @@ -336,51 +447,67 @@ class phpbb_functional_test_case extends phpbb_test_case global $phpbb_root_path; $db = $this->get_db(); - $sql = 'INSERT INTO ' . STYLES_TABLE . ' ' . $db->sql_build_array('INSERT', array( - 'style_id' => $style_id, - 'style_name' => $style_path, - 'style_copyright' => '', - 'style_active' => 1, - 'template_id' => $style_id, - 'theme_id' => $style_id, - 'imageset_id' => $style_id, - )); - $db->sql_query($sql); - - $sql = 'INSERT INTO ' . STYLES_IMAGESET_TABLE . ' ' . $db->sql_build_array('INSERT', array( - 'imageset_id' => $style_id, - 'imageset_name' => $style_path, - 'imageset_copyright' => '', - 'imageset_path' => $style_path, - )); - $db->sql_query($sql); - - $sql = 'INSERT INTO ' . STYLES_TEMPLATE_TABLE . ' ' . $db->sql_build_array('INSERT', array( - 'template_id' => $style_id, - 'template_name' => $style_path, - 'template_copyright' => '', - 'template_path' => $style_path, - 'bbcode_bitfield' => 'kNg=', - 'template_inherits_id' => $parent_style_id, - 'template_inherit_path' => $parent_style_path, - )); - $db->sql_query($sql); - - $sql = 'INSERT INTO ' . STYLES_THEME_TABLE . ' ' . $db->sql_build_array('INSERT', array( - 'theme_id' => $style_id, - 'theme_name' => $style_path, - 'theme_copyright' => '', - 'theme_path' => $style_path, - 'theme_storedb' => 0, - 'theme_mtime' => 0, - 'theme_data' => '', - )); - $db->sql_query($sql); - - if ($style_path != 'prosilver' && $style_path != 'subsilver2') + if (version_compare(PHPBB_VERSION, '3.1.0-dev', '<')) + { + $sql = 'INSERT INTO ' . STYLES_TABLE . ' ' . $db->sql_build_array('INSERT', array( + 'style_id' => $style_id, + 'style_name' => $style_path, + 'style_copyright' => '', + 'style_active' => 1, + 'template_id' => $style_id, + 'theme_id' => $style_id, + 'imageset_id' => $style_id, + )); + $db->sql_query($sql); + + $sql = 'INSERT INTO ' . STYLES_IMAGESET_TABLE . ' ' . $db->sql_build_array('INSERT', array( + 'imageset_id' => $style_id, + 'imageset_name' => $style_path, + 'imageset_copyright' => '', + 'imageset_path' => $style_path, + )); + $db->sql_query($sql); + + $sql = 'INSERT INTO ' . STYLES_TEMPLATE_TABLE . ' ' . $db->sql_build_array('INSERT', array( + 'template_id' => $style_id, + 'template_name' => $style_path, + 'template_copyright' => '', + 'template_path' => $style_path, + 'bbcode_bitfield' => 'kNg=', + 'template_inherits_id' => $parent_style_id, + 'template_inherit_path' => $parent_style_path, + )); + $db->sql_query($sql); + + $sql = 'INSERT INTO ' . STYLES_THEME_TABLE . ' ' . $db->sql_build_array('INSERT', array( + 'theme_id' => $style_id, + 'theme_name' => $style_path, + 'theme_copyright' => '', + 'theme_path' => $style_path, + 'theme_storedb' => 0, + 'theme_mtime' => 0, + 'theme_data' => '', + )); + $db->sql_query($sql); + + if ($style_path != 'prosilver' && $style_path != 'subsilver2') + { + @mkdir($phpbb_root_path . 'styles/' . $style_path, 0777); + @mkdir($phpbb_root_path . 'styles/' . $style_path . '/template', 0777); + } + } + else { - @mkdir($phpbb_root_path . 'styles/' . $style_path, 0777); - @mkdir($phpbb_root_path . 'styles/' . $style_path . '/template', 0777); + $db->sql_multi_insert(STYLES_TABLE, array(array( + 'style_id' => $style_id, + 'style_name' => $style_path, + 'style_copyright' => '', + 'style_active' => 1, + 'style_path' => $style_path, + 'bbcode_bitfield' => 'kNg=', + 'style_parent_id' => $parent_style_id, + 'style_parent_tree' => $parent_style_path, + ))); } } @@ -396,14 +523,17 @@ class phpbb_functional_test_case extends phpbb_test_case $db = $this->get_db(); $db->sql_query('DELETE FROM ' . STYLES_TABLE . ' WHERE style_id = ' . $style_id); - $db->sql_query('DELETE FROM ' . STYLES_IMAGESET_TABLE . ' WHERE imageset_id = ' . $style_id); - $db->sql_query('DELETE FROM ' . STYLES_TEMPLATE_TABLE . ' WHERE template_id = ' . $style_id); - $db->sql_query('DELETE FROM ' . STYLES_THEME_TABLE . ' WHERE theme_id = ' . $style_id); - - if ($style_path != 'prosilver' && $style_path != 'subsilver2') + if (version_compare(PHPBB_VERSION, '3.1.0-dev', '<')) { - @rmdir($phpbb_root_path . 'styles/' . $style_path . '/template'); - @rmdir($phpbb_root_path . 'styles/' . $style_path); + $db->sql_query('DELETE FROM ' . STYLES_IMAGESET_TABLE . ' WHERE imageset_id = ' . $style_id); + $db->sql_query('DELETE FROM ' . STYLES_TEMPLATE_TABLE . ' WHERE template_id = ' . $style_id); + $db->sql_query('DELETE FROM ' . STYLES_THEME_TABLE . ' WHERE theme_id = ' . $style_id); + + if ($style_path != 'prosilver' && $style_path != 'subsilver2') + { + @rmdir($phpbb_root_path . 'styles/' . $style_path . '/template'); + @rmdir($phpbb_root_path . 'styles/' . $style_path); + } } } @@ -418,10 +548,7 @@ class phpbb_functional_test_case extends phpbb_test_case // Required by unique_id global $config; - if (!is_array($config)) - { - $config = array(); - } + $config = new \phpbb\config\config(array()); /* * Add required config entries to the config array to prevent @@ -436,7 +563,7 @@ class phpbb_functional_test_case extends phpbb_test_case $config['rand_seed_last_update'] = time() + 600; // Required by user_add - global $db, $cache; + global $db, $cache, $phpbb_dispatcher, $phpbb_container; $db = $this->get_db(); if (!function_exists('phpbb_mock_null_cache')) { @@ -444,6 +571,12 @@ class phpbb_functional_test_case extends phpbb_test_case } $cache = new phpbb_mock_null_cache; + $cache_driver = new \phpbb\cache\driver\null(); + $phpbb_container = new phpbb_mock_container_builder(); + $phpbb_container->set('cache.driver', $cache_driver); + $phpbb_notifications = new phpbb_mock_notification_manager(); + $phpbb_container->set('notification_manager', $phpbb_notifications); + if (!function_exists('utf_clean_string')) { require_once(__DIR__ . '/../../phpBB/includes/utf/utf_tools.php'); @@ -452,6 +585,10 @@ class phpbb_functional_test_case extends phpbb_test_case { require_once(__DIR__ . '/../../phpBB/includes/functions_user.php'); } + set_config(null, null, null, $config); + set_config_count(null, null, null, $config); + $phpbb_dispatcher = new phpbb_mock_event_dispatcher(); + $passwords_manager = $this->get_passwords_manager(); $user_row = array( 'username' => $username, @@ -459,13 +596,94 @@ class phpbb_functional_test_case extends phpbb_test_case 'user_email' => 'nobody@example.com', 'user_type' => 0, 'user_lang' => 'en', - 'user_timezone' => 0, - 'user_dateformat' => '', - 'user_password' => phpbb_hash($username . $username), + 'user_timezone' => 'UTC', + 'user_dateformat' => 'r', + 'user_password' => $passwords_manager->hash($username . $username), ); return user_add($user_row); } + protected function remove_user_group($group_name, $usernames) + { + global $db, $cache, $auth, $config, $phpbb_dispatcher, $phpbb_log, $phpbb_container, $phpbb_root_path, $phpEx; + + $config = new \phpbb\config\config(array()); + $config['coppa_enable'] = 0; + + $db = $this->get_db(); + $phpbb_dispatcher = new phpbb_mock_event_dispatcher(); + $user = $this->getMock('\phpbb\user', array(), array('\phpbb\datetime')); + $auth = $this->getMock('\phpbb\auth\auth'); + + $phpbb_log = new \phpbb\log\log($db, $user, $auth, $phpbb_dispatcher, $phpbb_root_path, 'adm/', $phpEx, LOG_TABLE); + $cache = new phpbb_mock_null_cache; + + $cache_driver = new \phpbb\cache\driver\null(); + $phpbb_container = new phpbb_mock_container_builder(); + $phpbb_container->set('cache.driver', $cache_driver); + $phpbb_container->set('notification_manager', new phpbb_mock_notification_manager()); + + if (!function_exists('utf_clean_string')) + { + require_once(__DIR__ . '/../../phpBB/includes/utf/utf_tools.php'); + } + if (!function_exists('group_user_del')) + { + require_once(__DIR__ . '/../../phpBB/includes/functions_user.php'); + } + + $sql = 'SELECT group_id + FROM ' . GROUPS_TABLE . " + WHERE group_name = '" . $db->sql_escape($group_name) . "'"; + $result = $db->sql_query($sql); + $group_id = (int) $db->sql_fetchfield('group_id'); + $db->sql_freeresult($result); + + return group_user_del($group_id, false, $usernames, $group_name); + } + + protected function add_user_group($group_name, $usernames, $default = false, $leader = false) + { + global $db, $cache, $auth, $config, $phpbb_dispatcher, $phpbb_log, $phpbb_container, $phpbb_root_path, $phpEx; + + $config = new \phpbb\config\config(array()); + $config['coppa_enable'] = 0; + + $db = $this->get_db(); + $phpbb_dispatcher = new phpbb_mock_event_dispatcher(); + $user = $this->getMock('\phpbb\user', array(), array('\phpbb\datetime')); + $auth = $this->getMock('\phpbb\auth\auth'); + + $phpbb_log = new \phpbb\log\log($db, $user, $auth, $phpbb_dispatcher, $phpbb_root_path, 'adm/', $phpEx, LOG_TABLE); + $cache = new phpbb_mock_null_cache; + + $cache_driver = new \phpbb\cache\driver\null(); + $phpbb_container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $phpbb_container + ->expects($this->any()) + ->method('get') + ->with('cache.driver') + ->will($this->returnValue($cache_driver)); + + if (!function_exists('utf_clean_string')) + { + require_once(__DIR__ . '/../../phpBB/includes/utf/utf_tools.php'); + } + if (!function_exists('group_user_del')) + { + require_once(__DIR__ . '/../../phpBB/includes/functions_user.php'); + } + + $sql = 'SELECT group_id + FROM ' . GROUPS_TABLE . " + WHERE group_name = '" . $db->sql_escape($group_name) . "'"; + $result = $db->sql_query($sql); + $group_id = (int) $db->sql_fetchfield('group_id'); + $db->sql_freeresult($result); + + return group_user_add($group_id, false, $usernames, $group_name, $default, $leader); + } + protected function login($username = 'admin') { $this->add_lang('ucp'); @@ -475,7 +693,7 @@ class phpbb_functional_test_case extends phpbb_test_case $form = $crawler->selectButton($this->lang('LOGIN'))->form(); $crawler = self::submit($form, array('username' => $username, 'password' => $username . $username)); - $this->assertContains($this->lang('LOGIN_REDIRECT'), $crawler->filter('html')->text()); + $this->assertNotContains($this->lang('LOGIN'), $crawler->filter('.navbar')->text()); $cookies = self::$cookieJar->all(); @@ -489,6 +707,16 @@ class phpbb_functional_test_case extends phpbb_test_case } } + protected function logout() + { + $this->add_lang('ucp'); + + $crawler = self::request('GET', 'ucp.php?sid=' . $this->sid . '&mode=logout'); + $this->assertContains($this->lang('REGISTER'), $crawler->filter('.navbar')->text()); + unset($this->sid); + + } + /** * Login to the ACP * You must run login() before calling this. @@ -514,7 +742,7 @@ class phpbb_functional_test_case extends phpbb_test_case if (strpos($field, 'password_') === 0) { $crawler = self::submit($form, array('username' => $username, $field => $username . $username)); - $this->assertContains($this->lang('LOGIN_ADMIN_SUCCESS'), $crawler->filter('html')->text()); + $this->assertContains($this->lang('ADMIN_PANEL'), $crawler->filter('h1')->text()); $cookies = self::$cookieJar->all(); @@ -554,6 +782,30 @@ class phpbb_functional_test_case extends phpbb_test_case $this->lang = array_merge($this->lang, $lang); } + protected function add_lang_ext($ext_name, $lang_file) + { + if (is_array($lang_file)) + { + foreach ($lang_file as $file) + { + $this->add_lang_ext($ext_name, $file); + } + + return; + } + + $lang_path = __DIR__ . "/../../phpBB/ext/{$ext_name}/language/en/$lang_file.php"; + + $lang = array(); + + if (file_exists($lang_path)) + { + include($lang_path); + } + + $this->lang = array_merge($this->lang, $lang); + } + protected function lang() { $args = func_get_args(); @@ -570,6 +822,30 @@ class phpbb_functional_test_case extends phpbb_test_case } /** + * assertContains for language strings + * + * @param string $needle Search string + * @param string $haystack Search this + * @param string $message Optional failure message + */ + public function assertContainsLang($needle, $haystack, $message = null) + { + $this->assertContains(html_entity_decode($this->lang($needle), ENT_QUOTES), $haystack, $message); + } + + /** + * assertNotContains for language strings + * + * @param string $needle Search string + * @param string $haystack Search this + * @param string $message Optional failure message + */ + public function assertNotContainsLang($needle, $haystack, $message = null) + { + $this->assertNotContains(html_entity_decode($this->lang($needle), ENT_QUOTES), $haystack, $message); + } + + /* * Perform some basic assertions for the page * * Checks for debug/error output before the actual page content and the status code @@ -579,15 +855,36 @@ class phpbb_functional_test_case extends phpbb_test_case */ static public function assert_response_html($status_code = 200) { + // Any output before the doc type means there was an error + $content = self::$client->getResponse()->getContent(); + self::assertNotContains('[phpBB Debug]', $content); + self::assertStringStartsWith('<!DOCTYPE', trim($content), 'Output found before DOCTYPE specification.'); + if ($status_code !== false) { self::assert_response_status_code($status_code); } + } - // Any output before the doc type means there was an error + /* + * Perform some basic assertions for an xml page + * + * Checks for debug/error output before the actual page content and the status code + * + * @param mixed $status_code Expected status code, false to disable check + * @return null + */ + static public function assert_response_xml($status_code = 200) + { + // Any output before the xml opening means there was an error $content = self::$client->getResponse()->getContent(); self::assertNotContains('[phpBB Debug]', $content); - self::assertStringStartsWith('<!DOCTYPE', trim($content), 'Output found before DOCTYPE specification.'); + self::assertStringStartsWith('<?xml', trim($content), 'Output found before XML specification.'); + + if ($status_code !== false) + { + self::assert_response_status_code($status_code); + } } /** @@ -604,6 +901,86 @@ class phpbb_functional_test_case extends phpbb_test_case self::assertEquals($status_code, self::$client->getResponse()->getStatus(), 'HTTP status code does not match'); } + public function assert_filter($crawler, $expr, $msg = null) + { + $nodes = $crawler->filter($expr); + if ($msg) + { + $msg .= "\n"; + } + else + { + $msg = ''; + } + $msg .= "`$expr` not found in DOM."; + $this->assertGreaterThan(0, count($nodes), $msg); + return $nodes; + } + + /** + * Asserts that exactly one checkbox with name $name exists within the scope + * of $crawler and that the checkbox is checked. + * + * @param Symfony\Component\DomCrawler\Crawler $crawler + * @param string $name + * @param string $message + * + * @return null + */ + public function assert_checkbox_is_checked($crawler, $name, $message = '') + { + $this->assertSame( + 'checked', + $this->assert_find_one_checkbox($crawler, $name)->attr('checked'), + $message ?: "Failed asserting that checkbox $name is checked." + ); + } + + /** + * Asserts that exactly one checkbox with name $name exists within the scope + * of $crawler and that the checkbox is unchecked. + * + * @param Symfony\Component\DomCrawler\Crawler $crawler + * @param string $name + * @param string $message + * + * @return null + */ + public function assert_checkbox_is_unchecked($crawler, $name, $message = '') + { + $this->assertSame( + '', + $this->assert_find_one_checkbox($crawler, $name)->attr('checked'), + $message ?: "Failed asserting that checkbox $name is unchecked." + ); + } + + /** + * Searches for an input element of type checkbox with the name $name using + * $crawler. Contains an assertion that only one such checkbox exists within + * the scope of $crawler. + * + * @param Symfony\Component\DomCrawler\Crawler $crawler + * @param string $name + * @param string $message + * + * @return Symfony\Component\DomCrawler\Crawler + */ + public function assert_find_one_checkbox($crawler, $name, $message = '') + { + $query = sprintf('//input[@type="checkbox" and @name="%s"]', $name); + $result = $crawler->filterXPath($query); + + $this->assertEquals( + 1, + sizeof($result), + $message ?: 'Failed asserting that exactly one checkbox with name' . + " $name exists in crawler scope." + ); + + return $result; + } + /** * Creates a topic * @@ -613,9 +990,10 @@ class phpbb_functional_test_case extends phpbb_test_case * @param string $subject * @param string $message * @param array $additional_form_data Any additional form data to be sent in the request - * @return array post_id, topic_id + * @param string $expected Lang var of expected message after posting + * @return array|null post_id, topic_id if message is empty */ - public function create_topic($forum_id, $subject, $message, $additional_form_data = array()) + public function create_topic($forum_id, $subject, $message, $additional_form_data = array(), $expected = '') { $posting_url = "posting.php?mode=post&f={$forum_id}&sid={$this->sid}"; @@ -625,7 +1003,7 @@ class phpbb_functional_test_case extends phpbb_test_case 'post' => true, ), $additional_form_data); - return self::submit_post($posting_url, 'POST_TOPIC', $form_data); + return self::submit_post($posting_url, 'POST_TOPIC', $form_data, $expected); } /** @@ -638,9 +1016,10 @@ class phpbb_functional_test_case extends phpbb_test_case * @param string $subject * @param string $message * @param array $additional_form_data Any additional form data to be sent in the request - * @return array post_id, topic_id + * @param string $expected Lang var of expected message after posting + * @return array|null post_id, topic_id if message is empty */ - public function create_post($forum_id, $topic_id, $subject, $message, $additional_form_data = array()) + public function create_post($forum_id, $topic_id, $subject, $message, $additional_form_data = array(), $expected = '') { $posting_url = "posting.php?mode=reply&f={$forum_id}&t={$topic_id}&sid={$this->sid}"; @@ -650,7 +1029,7 @@ class phpbb_functional_test_case extends phpbb_test_case 'post' => true, ), $additional_form_data); - return self::submit_post($posting_url, 'POST_REPLY', $form_data); + return self::submit_post($posting_url, 'POST_REPLY', $form_data, $expected); } /** @@ -659,16 +1038,29 @@ class phpbb_functional_test_case extends phpbb_test_case * @param string $posting_url * @param string $posting_contains * @param array $form_data - * @return array post_id, topic_id + * @param string $expected Lang var of expected message after posting + * @return array|null post_id, topic_id if message is empty */ - protected function submit_post($posting_url, $posting_contains, $form_data) + protected function submit_post($posting_url, $posting_contains, $form_data, $expected = '') { $this->add_lang('posting'); $crawler = $this->submit_message($posting_url, $posting_contains, $form_data); - $this->assertContains($this->lang('POST_STORED'), $crawler->filter('html')->text()); - $url = $crawler->selectLink($this->lang('VIEW_MESSAGE', '', ''))->link()->getUri(); + if ($expected !== '') + { + if (isset($this->lang[$expected])) + { + $this->assertContainsLang($expected, $crawler->filter('html')->text()); + } + else + { + $this->assertContains($expected, $crawler->filter('html')->text()); + } + return null; + } + + $url = $crawler->selectLink($form_data['subject'])->link()->getUri(); return array( 'topic_id' => $this->get_parameter_from_link($url, 't'), @@ -722,10 +1114,33 @@ class phpbb_functional_test_case extends phpbb_test_case */ protected function submit_message($posting_url, $posting_contains, $form_data) { + if (time() == self::$last_post_timestamp) + { + // Travis is too fast, so we have to wait to not mix up the post/topic order + sleep(1); + } + self::$last_post_timestamp = time(); $crawler = self::request('GET', $posting_url); $this->assertContains($this->lang($posting_contains), $crawler->filter('html')->text()); + if (!empty($form_data['upload_files'])) + { + for ($i = 0; $i < $form_data['upload_files']; $i++) + { + $file = array( + 'tmp_name' => __DIR__ . '/../functional/fixtures/files/valid.jpg', + 'name' => 'valid.jpg', + 'type' => 'image/jpeg', + 'size' => filesize(__DIR__ . '/../functional/fixtures/files/valid.jpg'), + 'error' => UPLOAD_ERR_OK, + ); + + $crawler = self::$client->request('POST', $posting_url, array('add_file' => $this->lang('ADD_FILE')), array('fileupload' => $file)); + } + unset($form_data['upload_files']); + } + $hidden_fields = array( $crawler->filter('[type="hidden"]')->each(function ($node, $i) { return array('name' => $node->attr('name'), 'value' => $node->attr('value')); @@ -751,6 +1166,48 @@ class phpbb_functional_test_case extends phpbb_test_case } /** + * Deletes a topic + * + * Be sure to login before creating + * + * @param int $topic_id + * @return null + */ + public function delete_topic($topic_id) + { + $this->add_lang('posting'); + $crawler = $this->get_quickmod_page($topic_id, 'DELETE_TOPIC'); + $this->assertContainsLang('DELETE_PERMANENTLY', $crawler->text()); + + $this->add_lang('mcp'); + $form = $crawler->selectButton('Yes')->form(); + $form['delete_permanent'] = 1; + $crawler = self::submit($form); + $this->assertContainsLang('TOPIC_DELETED_SUCCESS', $crawler->text()); + } + + /** + * Deletes a post + * + * Be sure to login before creating + * + * @param int $forum_id + * @param int $topic_id + * @return null + */ + public function delete_post($forum_id, $post_id) + { + $this->add_lang('posting'); + $crawler = self::request('GET', "posting.php?mode=delete&f={$forum_id}&p={$post_id}&sid={$this->sid}"); + $this->assertContainsLang('DELETE_PERMANENTLY', $crawler->text()); + + $form = $crawler->selectButton('Yes')->form(); + $form['delete_permanent'] = 1; + $crawler = self::submit($form); + $this->assertContainsLang('POST_DELETED', $crawler->text()); + } + + /** * Returns the requested parameter from a URL * * @param string $url @@ -785,4 +1242,50 @@ class phpbb_functional_test_case extends phpbb_test_case } return null; } + + /** + * Return a passwords manager instance + * + * @return phpbb\passwords\manager + */ + public function get_passwords_manager() + { + // Prepare dependencies for manager and driver + $config = new \phpbb\config\config(array()); + $driver_helper = new \phpbb\passwords\driver\helper($config); + + $passwords_drivers = array( + 'passwords.driver.bcrypt_2y' => new \phpbb\passwords\driver\bcrypt_2y($config, $driver_helper), + 'passwords.driver.bcrypt' => new \phpbb\passwords\driver\bcrypt($config, $driver_helper), + 'passwords.driver.salted_md5' => new \phpbb\passwords\driver\salted_md5($config, $driver_helper), + 'passwords.driver.phpass' => new \phpbb\passwords\driver\phpass($config, $driver_helper), + ); + + $passwords_helper = new \phpbb\passwords\helper; + // Set up passwords manager + $manager = new \phpbb\passwords\manager($config, $passwords_drivers, $passwords_helper, array_keys($passwords_drivers)); + + return $manager; + } + + /** + * Get quickmod page + * + * @param int $topic_id + * @param string $action Language key for the quickmod action + * @param Symfony\Component\DomCrawler\Crawler Optional crawler object to use instead of creating new one. + * @return Symfony\Component\DomCrawler\Crawler + */ + public function get_quickmod_page($topic_id, $action, $crawler = false) + { + $this->add_lang('viewtopic'); + + if ($crawler === false) + { + $crawler = self::request('GET', "viewtopic.php?t={$topic_id}&sid={$this->sid}"); + } + $link = $crawler->filter('#quickmod')->selectLink($this->lang($action))->link()->getUri(); + + return self::request('GET', substr($link, strpos($link, 'mcp.'))); + } } |