- •Contents at a Glance
- •Contents
- •Foreword
- •About the Authors
- •About the Technical Reviewers
- •Acknowledgments
- •Introduction
- •How Drupal Works
- •What Is Drupal?
- •Technology Stack
- •Core
- •Administrative Interface
- •Modules
- •Hooks
- •Themes
- •Nodes
- •Fields
- •Blocks
- •File Layout
- •Serving a Request
- •The Web Server’s Role
- •The Bootstrap Process
- •Processing a Request
- •Theming the Data
- •Summary
- •Writing a Module
- •Creating the Files
- •Implementing a Hook
- •Adding Module-Specific Settings
- •Defining Your Own Administration Section
- •Presenting a Settings Form to the User
- •Validating User-Submitted Settings
- •Storing Settings
- •Using Drupal’s variables Table
- •Retrieving Stored Values with variable_get()
- •Further Steps
- •Summary
- •Hooks, Actions, and Triggers
- •Understanding Events and Triggers
- •Understanding Actions
- •The Trigger User Interface
- •Your First Action
- •Assigning the Action
- •Changing Which Triggers an Action Supports
- •Actions That Support Any Trigger
- •Advanced Actions
- •Using the Context in Actions
- •How the Trigger Module Prepares the Context
- •Changing Existing Actions with action_info_alter()
- •Establishing the Context
- •How Actions Are Stored
- •The actions Table
- •Action IDs
- •Calling an Action Directly with actions_do()
- •Defining Your Own Triggers with hook_trigger_info()
- •Adding Triggers to Existing Hooks
- •Summary
- •The Menu System
- •Callback Mapping
- •Mapping URLs to Functions
- •Creating a Menu Item
- •Page Callback Arguments
- •Page Callbacks in Other Files
- •Adding a Link to the Navigation Block
- •Menu Nesting
- •Access Control
- •Title Localization and Customization
- •Defining a Title Callback
- •Wildcards in Menu Items
- •Basic Wildcards
- •Wildcards and Page Callback Parameters
- •Using the Value of a Wildcard
- •Wildcards and Parameter Replacement
- •Passing Additional Arguments to the Load Function
- •Special, Predefined Load Arguments: %map and %index
- •Building Paths from Wildcards Using to_arg() Functions
- •Special Cases for Wildcards and to_arg() Functions
- •Altering Menu Items from Other Modules
- •Altering Menu Links from Other Modules
- •Kinds of Menu Items
- •Common Tasks
- •Assigning Callbacks Without Adding a Link to the Menu
- •Displaying Menu Items As Tabs
- •Hiding Existing Menu Items
- •Using menu.module
- •Common Mistakes
- •Summary
- •Working with Databases
- •Defining Database Parameters
- •Understanding the Database Abstraction Layer
- •Connecting to the Database
- •Performing Simple Queries
- •Retrieving Query Results
- •Getting a Single Value
- •Getting Multiple Rows
- •Using the Query Builder and Query Objects
- •Getting a Limited Range of Results
- •Getting Results for Paged Display
- •Other Common Queries
- •Inserts and Updates with drupal_write_record()
- •The Schema API
- •Using Module .install Files
- •Creating Tables
- •Using the Schema Module
- •Field Type Mapping from Schema to Database
- •Textual
- •Varchar
- •Char
- •Text
- •Numerical
- •Integer
- •Serial
- •Float
- •Numeric
- •Date and Time: Datetime
- •Binary: Blob
- •Declaring a Specific Column Type with mysql_type
- •Maintaining Tables
- •Deleting Tables on Uninstall
- •Changing Existing Schemas with hook_schema_alter()
- •Modifying Other Modules’ Queries with hook_query_alter()
- •Connecting to Multiple Databases Within Drupal
- •Using a Temporary Table
- •Writing Your Own Database Driver
- •Summary
- •Working with Users
- •The $user Object
- •Testing If a User Is Logged In
- •Introduction to user hooks
- •Understanding hook_user_view($account, $view_mode)
- •The User Registration Process
- •Using profile.module to Collect User Information
- •The Login Process
- •Adding Data to the $user Object at Load Time
- •Providing User Information Categories
- •External Login
- •Summary
- •Working with Nodes
- •So What Exactly Is a Node?
- •Not Everything Is a Node
- •Creating a Node Module
- •Creating the .install File
- •Creating the .info File
- •Creating the .module File
- •Providing Information About Our Node Type
- •Modifying the Menu Callback
- •Defining Node-Type–Specific Permissions with hook_permission()
- •Limiting Access to a Node Type with hook__node_access()
- •Customizing the Node Form for Our Node Type
- •Validating Fields with hook_validate()
- •Saving Our Data with hook_insert()
- •Keeping Data Current with hook_update()
- •Cleaning Up with hook_delete()
- •Modifying Nodes of Our Type with hook_load()
- •Using hook_view()
- •Manipulating Nodes That Are Not Our Type with hook_node_xxxxx()
- •How Nodes Are Stored
- •Creating a Node Type with Custom Content Types
- •Restricting Access to Nodes
- •Defining Node Grants
- •What Is a Realm?
- •What Is a Grant ID?
- •The Node Access Process
- •Summary
- •Working with Fields
- •Creating Content Types
- •Adding Fields to a Content Type
- •Creating a Custom Field
- •Adding Fields Programmatically
- •Summary
- •The Theme System
- •Themes
- •Installing an Off-the-Shelf Theme
- •Building a Theme
- •The .info File
- •Adding Regions to Your Theme
- •Adding CSS Files to Your Theme
- •Adding JavaScript Files
- •Adding Settings to Your Theme
- •Understanding Template Files
- •The Big Picture
- •The html.php.tpl File
- •The page.tpl.php File
- •The region.tpl.php File
- •The node.tpl.php File
- •The field.tpl.php File
- •The block.tpl.php File
- •Overriding Template Files
- •Other Template Files
- •Introducing the theme() Function
- •An Overview of How theme() Works
- •Overriding Themable Items
- •Overriding with Template Files
- •Adding and Manipulating Template Variables
- •Using the Theme Developer Module
- •Summary
- •Working with Blocks
- •What Is a Block?
- •Block Configuration Options
- •Block Placement
- •Defining a Block
- •Using the Block Hooks
- •Building a Block
- •Enabling a Block When a Module Is Installed
- •Block Visibility Examples
- •Displaying a Block to Logged-In Users Only
- •Displaying a Block to Anonymous Users Only
- •Summary
- •The Form API
- •Understanding Form Processing
- •Initializing the Process
- •Setting a Token
- •Setting an ID
- •Collecting All Possible Form Element Definitions
- •Looking for a Validation Function
- •Looking for a Submit Function
- •Allowing Modules to Alter the Form Before It’s Built
- •Building the Form
- •Allowing Functions to Alter the Form After It’s Built
- •Checking If the Form Has Been Submitted
- •Finding a Theme Function for the Form
- •Allowing Modules to Modify the Form Before It’s Rendered
- •Rendering the Form
- •Validating the Form
- •Token Validation
- •Built-In Validation
- •Element-Specific Validation
- •Validation Callbacks
- •Submitting the Form
- •Redirecting the User
- •Creating Basic Forms
- •Form Properties
- •Form IDs
- •Fieldsets
- •Theming Forms
- •Using #prefix, #suffix, and #markup
- •Using a Theme Function
- •Telling Drupal Which Theme Function to Use
- •Specifying Validation and Submission Functions with hook_forms()
- •Call Order of Theme, Validation, and Submission Functions
- •Writing a Validation Function
- •Form Rebuilding
- •Writing a Submit Function
- •Changing Forms with hook_form_alter()
- •Altering Any Form
- •Altering a Specific Form
- •Submitting Forms Programmatically with drupal_form_submit()
- •Dynamic Forms
- •Form API Properties
- •Properties for the Root of the Form
- •#action
- •#built
- •#method
- •Properties Added to All Elements
- •#description
- •#attributes
- •#required
- •#tree
- •Properties Allowed in All Elements
- •#type
- •#access
- •#after_build
- •#array_parents
- •#attached
- •#default_value
- •#disabled
- •#element_validate
- •#parents
- •#post_render
- •#prefix
- •#pre_render
- •#process
- •#states
- •#suffix
- •#theme
- •#theme_wrappers
- •#title
- •#tree
- •#weight
- •Form Elements
- •Text Field
- •Password
- •Password with Confirmation
- •Textarea
- •Select
- •Radio Buttons
- •Check Boxes
- •Value
- •Hidden
- •Date
- •Weight
- •File Upload
- •Fieldset
- •Submit
- •Button
- •Image Button
- •Markup
- •Item
- •#ajax Property
- •Summary
- •Filters
- •Filters and Text formats
- •Installing a Filter
- •Knowing When to Use Filters
- •Creating a Custom Filter
- •Implementing hook_filter_info()
- •The Process Function
- •Helper Function
- •Summary
- •Searching and Indexing Content
- •Building a Custom Search Page
- •The Default Search Form
- •The Advanced Search Form
- •Adding to the Search Form
- •Introducing the Search Hooks
- •Formatting Search Results with hook_search_page()
- •Making Path Aliases Searchable
- •Using the Search HTML Indexer
- •When to Use the Indexer
- •How the Indexer Works
- •Adding Metadata to Nodes: hook_node_update_index()
- •Indexing Content That Isn’t a Node: hook_update_index()
- •Summary
- •Working with Files
- •How Drupal Serves Files
- •Managed and Unmanaged Drupal APIs
- •Public Files
- •Private Files
- •PHP Settings
- •Media Handling
- •Upload Field
- •Video and Audio
- •File API
- •Database Schema
- •Common Tasks and Functions
- •Finding the Default Files URI
- •Copying and Moving Files
- •Checking Directories
- •Uploading Files
- •Getting the URL for a File
- •Finding Files in a Directory
- •Finding the Temp Directory
- •Neutralizing Dangerous Files
- •Checking Disk Space
- •Authentication Hooks for Downloading
- •Summary
- •Working with Taxonomy
- •The Structure of Taxonomy
- •Creating a Vocabulary
- •Creating Terms
- •Assigning a Vocabulary to a Content Type
- •Kinds of Taxonomy
- •Flat
- •Hierarchical
- •Multiple Hierarchical
- •Viewing Content by Term
- •Using AND and OR in URLs
- •Specifying Depth for Hierarchical Vocabularies
- •Automatic RSS Feeds
- •Storing Taxonomies
- •Module-Based Vocabularies
- •Creating a Module-Based Vocabulary
- •Keeping Informed of Vocabulary Changes with Taxonomy Hooks
- •Common Tasks
- •Displaying Taxonomy Terms Associated with a Node
- •Building Your Own Taxonomy Queries
- •Using taxonomy_select_nodes()
- •Taxonomy Functions
- •Retrieving Information About Vocabularies
- •taxonomy_vocabulary_load($vid)
- •taxonomy_get_vocabularies()
- •Adding, Modifying, and Deleting Vocabularies
- •taxonomy_vocabulary_save($vocabulary)
- •taxonomy_vocabulary_delete($vid)
- •Retrieving Information About Terms
- •taxonomy_load_term($tid)
- •taxonomy_get_term_by_name($name)
- •Adding, Modifying, and Deleting Terms
- •taxonomy_term_save($term)
- •taxonomy_term_delete($tid)
- •Retrieving Information About Term Hierarchy
- •taxonomy_get_parents($tid, $key)
- •taxonomy_get_parents_all($tid)
- •taxonomy_get_children($tid, $vid, $key)
- •taxonomy_get_tree($vid, $parent, $max_depth, $load_entities = FALSE)
- •Finding Nodes with Certain Terms
- •Additional Resources
- •Summary
- •Caching
- •Knowing When to Cache
- •How Caching Works
- •How Caching Is Used Within Drupal Core
- •Menu System
- •Caching Filtered Text
- •Administration Variables and Module Settings
- •Disabling Caching
- •Page Caching
- •Static Page Caching
- •Blocks
- •Using the Cache API
- •Caching Data with cache_set()
- •Retrieving Cached Data with cache_get() and cache_get_multiple()
- •Checking to See If Cache Is Empty with cache_is_empty()
- •Clearing Cache with cache_clear_all()
- •Summary
- •Sessions
- •What Are Sessions?
- •Usage
- •Session-Related Settings
- •In .htaccess
- •In settings.php
- •In bootstrap.inc
- •Requiring Cookies
- •Storage
- •Session Life Cycle
- •Session Conversations
- •First Visit
- •Second Visit
- •User with an Account
- •Common Tasks
- •Changing the Length of Time Before a Cookie Expires
- •Changing the Name of the Session
- •Storing Data in the Session
- •Summary
- •Using jQuery
- •What Is jQuery?
- •How jQuery Works
- •Using a CSS ID Selector
- •Using a CSS Class Selector
- •jQuery Within Drupal
- •Your First jQuery Code
- •Targeting an Element by ID
- •Method Chaining
- •Adding or Removing a Class
- •Wrapping Existing Elements
- •Changing Values of CSS Elements
- •Where to Put JavaScript
- •Adding JavaScript via a Theme .info File
- •A Module That Uses jQuery
- •Overridable JavaScript
- •Building a jQuery Voting Widget
- •Building the Module
- •Using Drupal.behaviors
- •Ways to Extend This Module
- •Compatibility
- •Next Steps
- •Summary
- •Localization and Translation
- •Enabling the Locale Module
- •User Interface Translation
- •Strings
- •Translating Strings with t()
- •Replacing Built-In Strings with Custom Strings
- •String Overrides in settings.php
- •Replacing Strings with the Locale Module
- •Exporting Your Translation
- •Starting a New Translation
- •Generating .pot Files with Translation Template Extractor
- •Creating a .pot File for Your Module
- •Using the Command Line
- •Using the Web-Based Extractor
- •Creating .pot Files for an Entire Site
- •Installing a Language Translation
- •Setting Up a Translation at Install Time
- •Installing a Translation on an Existing Site
- •Right-to-Left Language Support
- •Language Negotiation
- •Default
- •User-Preferred Language
- •The Global $language Object
- •Path Prefix Only
- •Path Prefix with Language Fallback
- •URL Only
- •Content Translation
- •Introducing the Content Translation Module
- •Multilingual Support
- •Multilingual Support with Translation
- •Localizationand Translation-Related Files
- •Additional Resources
- •Summary
- •What Is XML-RPC?
- •Prerequisites for XML-RPC
- •XML-RPC Clients
- •XML-RPC Client Example: Getting the Time
- •XML-RPC Client Example: Getting the Name of a State
- •Handling XML-RPC Client Errors
- •Network Errors
- •HTTP Errors
- •Call Syntax Errors
- •A Simple XML-RPC Server
- •Mapping Your Method with hook_xmlrpc()
- •Automatic Parameter Type Validation with hook_xmlrpc()
- •Built-In XML-RPC Methods
- •system.listMethods
- •system.methodSignature
- •system.methodHelp
- •system.getCapabilities
- •system.multiCall
- •Summary
- •Writing Secure Code
- •Handling User Input
- •Thinking About Data Types
- •Plain Text
- •HTML Text
- •Rich Text
- •Using check_plain() and t() to Sanitize Output
- •Using filter_xss() to Prevent Cross-Site Scripting Attacks
- •Using filter_xss_admin()
- •Handling URLs Securely
- •Making Queries Secure with db_query()
- •Keeping Private Data Private with hook_query_alter()
- •Dynamic Queries
- •Permissions and Page Callbacks
- •Cross-Site Request Forgeries (CSRF)
- •File Security
- •File Permissions
- •Protected Files
- •File Uploads
- •Filenames and Paths
- •Encoding Mail Headers
- •Files for Production Environments
- •SSL Support
- •Stand-Alone PHP
- •AJAX Security, a.k.a. Request Replay Attack
- •Form API Security
- •Protecting the Superuser Account
- •Summary
- •Development Best Practices
- •Coding Standards
- •Line Indention and Whitespace
- •Operators
- •Casting
- •Control Structures
- •Function Calls
- •Function Declarations
- •Function Names
- •Class Constructor Calls
- •Arrays
- •Quotes
- •String Concatenators
- •Comments
- •Documentation Examples
- •Documenting Constants
- •Documenting Functions
- •Documenting Hook Implementations
- •Including Code
- •PHP Code Tags
- •Semicolons
- •Example URLs
- •Naming Conventions
- •Checking Your Coding Style with Coder Module
- •Finding Your Way Around Code with grep
- •Summary
- •Optimizing Drupal
- •Caching Is the Key to Drupal Performance
- •Optimizing PHP
- •Setting PHP Opcode Cache File to /dev/zero
- •PHP Process Pool Settings
- •Tuning Apache
- •mod_expires
- •Moving Directives from .htaccess to httpd.conf
- •MPM Prefork vs. Apache MPM Worker
- •Balancing the Apache Pool Size
- •Decreasing Apache Timeout
- •Disabling Unused Apache Modules
- •Using Nginx Instead of Apache
- •Using Pressflow
- •Varnish
- •Normalizing incoming requests for better Varnish hits
- •Varnish: finding extraneous cookies
- •Boost
- •Boost vs. Varnish
- •Linux System Tuning for High Traffic Servers
- •Using Fast File Systems
- •Dedicated Servers vs. Virtual Servers
- •Avoiding Calling External Web Services
- •Decreasing Server Timeouts
- •Database Optimization
- •Enabling MySQL’s Query Cache
- •MySQL InnoDB Performance on Windows
- •Drupal Performance
- •Eliminating 404 Errors
- •Disabling Modules You’re Not Using
- •Drupal-Specific Optimizations
- •Page Caching
- •Bandwidth Optimization
- •Pruning the Sessions Table
- •Managing the Traffic of Authenticated Users
- •Logging to the Database
- •Logging to Syslog
- •Running cron
- •Architectures
- •Single Server
- •Separate Database Server
- •Separate Database Server and a Web Server Cluster
- •Load Balancing
- •File Uploads and Synchronization
- •Multiple Database Servers
- •Database Replication
- •Database Partitioning
- •Finding the Bottleneck
- •Web Server Running Out of CPU
- •Web Server Running Out of RAM
- •Identifying Expensive Database Queries
- •Identifying Expensive Pages
- •Identifying Expensive Code
- •Optimizing Tables
- •Caching Queries Manually
- •Changing the Table Type from MyISAM to InnoDB
- •Summary
- •Installation Profiles
- •Creating a New Installation Profile
- •The enhanced.info File
- •The enhanced.profile File
- •The enhanced.install File
- •Using hook_install_tasks and hook_install_tasks_alter
- •Summary
- •Testing
- •Setting Up the Test Environment
- •How Tests Are Defined
- •Test Functions
- •Test Assertions
- •Summary
- •Database Table Reference
- •Resources
- •Code
- •The Drupal Source Code Repository on GIT
- •Examples
- •Drupal API Reference
- •Security Advisories
- •Updating Modules
- •Updating Themes
- •Handbooks
- •Forums
- •Mailing Lists
- •Development
- •Themes
- •Translations
- •User Groups and Interest Groups
- •Internet Relay Chat
- •North America
- •Europe
- •Asia
- •Latin America / Caribbean
- •Oceania
- •Africa
- •Videocasts
- •Weblogs
- •Conferences
- •Contribute
- •Index
- •Numbers
Download from Wow! eBook <www.wowebook.com>
CHAPTER 25 ■ TESTING
The testing framework also provides the ability to display the values associated with variables and entities like a node object, providing further assistance in debugging why a test failed. If proof is in the pudding, then the Drupal testing framework is double chocolate pudding—rich and delicious.
How Tests Are Defined
Tests are typically associated with a module and as such are defined at the module level. In the foregoing case of the Blog test, the developers of the Blog module created a new file in the Blog module directory named blog.test. The content of the blog.test module shows how the developer set up the environment and the individual test conditions that will be executed when the test suite is run.
The first thing to notice is that testing is very object-oriented. A test is essentially an object that is created from a class that is based on the DrupalWebTestCase or DrupalUnitTestCase classes. By taking an object-oriented approach, our test class inherits all of the functionality defined in the base class, allowing you the developer to focus on what tests you want to run rather than coding scripts to handle things like loading a page, figuring out how to enter text on a form, etc.
The next step is to define any instance variables that will be used throughout the testing process. In the case of the Blog tests, the developer defined three variables, each representing a specific user with specific assigned privileges.
<?php
class BlogTestCase extends DrupalWebTestCase { protected $big_user;
protected $own_user; protected $any_user;
The next step is to define the name, description, and the group associated with this set of tests, using the getInfo function. This is the information that appears on the Configuration -> Testing page. The group field places this test in the Blog group. You can see the name and description of the test if you click the arrow to the left of Blog on the Tests page. This approach allows you to define logical sets of tests and associate those tests with a specific module.
public static function getInfo() { return array(
'name' => 'Blog functionality',
'description' => 'Create, view, edit, delete, and change blog entries and verify its consistency in the database.',
'group' => 'Blog',
);
}
The setup process is the next section defined by the developer. When the testing process begins, the first thing that the test framework does is to create a new base Drupal instance. If our tests require modules, user accounts, content types, files in the files directory, or anything else beyond a base Drupal 7 core install, the setUp() function is where you perform those setup tasks. In the case of the Blog tests, the only thing we need to do is to first enable the Blog module, which is performed through the parent:setUp(‘blog’) function call. If there were additional modules that needed to be enabled, you would add each module as an additional parameter such as parent::setUp('blog', 'ctools', 'panels',
'date').
550
CHAPTER 25 ■ TESTING
The next step creates the three user accounts we want to use, assigning specific permissions to each of the three accounts. We create a new user by executing the drupalCreateUser method that is associated with our current testing object—referenced as $this. To assign permissions, we simply pass an array of permissions that we want assigned to each user account.
/**
* Enable modules and create users with specific permissions. */
function setUp() { parent::setUp('blog'); // Create users.
$this->big_user = $this->drupalCreateUser(array('administer blocks')); $this->own_user = $this->drupalCreateUser(array('create blog content', 'edit own blog
content', 'delete own blog content'));
$this->any_user = $this->drupalCreateUser(array('create blog content', 'edit any blog content', 'delete any blog content', 'access administration pages'));
}
The next function acts as the primary controller for the execution of the tests.
■ Note Any function that starts with a test will be automatically discovered and executed.
In this function, the test script logs the admin user, big_user, into the site, followed by enabling the recent blog posting block and assigning it to the sidebar_second region. The next step configures the recent blog posts block, setting the number of posts that will appear in the block to five. The next step executes the doBasicTests function (defined later) using the any_user and own_user accounts, followed by execution of other test functions defined elsewhere in this test script.
/**
* Login users, create blog nodes, and test blog functionality through the admin and user interfaces.
*/
function testBlog() {
// Login the admin user. $this->drupalLogin($this->big_user);
//Enable the recent blog block. $edit = array();
$edit['blog_recent[region]'] = 'sidebar_second'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); $this->assertResponse(200);
//Verify ability to change number of recent blog posts in block.
$edit = array(); $edit['blog_block_count'] = 5;
$this->drupalPost('admin/structure/block/manage/blog/recent/configure', $edit, t('Save block'));
551
CHAPTER 25 ■ TESTING
$this->assertEqual(variable_get('blog_block_count', 10), 5, t('Number of recent blog posts changed.'));
// Do basic tests for each user. $this->doBasicTests($this->any_user, TRUE); $this->doBasicTests($this->own_user, FALSE);
// Create another blog node for the any blog user.
$node = $this->drupalCreateNode(array('type' => 'blog', 'uid' => $this->any_user->uid));
//Verify the own blog user only has access to the blog view node. $this->verifyBlogs($this->any_user, $node, FALSE, 403);
//Create another blog node for the own blog user.
$node = $this->drupalCreateNode(array('type' => 'blog', 'uid' => $this->own_user->uid)); // Login the any blog user.
$this->drupalLogin($this->any_user);
// Verify the any blog user has access to all the blog nodes. $this->verifyBlogs($this->own_user, $node, TRUE);
}
The next set of functions executes specific functional tests. The testUnprivilegedUser function checks to make sure that someone who shouldn’t have the ability to post a blog on the web site. The process for testing this functionality is first attempting to create a node using the drupalCreateNode method, which takes an array of parameters that are used to attempt to create a node. The next step is to log onto the site by using the drupalLogin method and the big_user that was created in the setup function. After the user is logged in, the next step is to navigate to the big_user’s blog page using the drupalGet functionality. The next step checks the HTTP response code returned from attempting to navigate to that user’s blog page. To test whether the user successfully landed on his or her own blog page, the developer used the assertResponse method to check the HTTP response code. If the code is 200 (success), then the test was successful and logged as such. The developer continues the test by checking to see if the page title is set to the user’s name and “blog” using the assertTitle method; if it is, then the success is logged. The final test is to see whether the text “You are not allowed to post a new blog entry” is displayed somewhere on the page. The developer uses the assertText function, which scans the page looking for an exact match of the string that is passed as the first parameter. If there is a match, then the test succeeded and the result is logged using the second parameter passed in the assertText method.
/**
*Confirm that the "You are not allowed to post a new blog entry." message
*shows up if a user submitted blog entries, has been denied that
*permission, and goes to the blog page.
*/
function testUnprivilegedUser() {
// Create a blog node for a user with no blog permissions. $this->drupalCreateNode(array('type' => 'blog', 'uid' => $this->big_user->uid));
$this->drupalLogin($this->big_user);
$this->drupalGet('blog/' . $this->big_user->uid); $this->assertResponse(200);
552
CHAPTER 25 ■ TESTING
$this->assertTitle(t("@name's blog", array('@name' => format_username($this->big_user)))
. ' | Drupal', t('Blog title was displayed'));
$this->assertText(t('You are not allowed to post a new blog entry.'), t('No new entries can be posted without the right permission'));
}
The next test checks to see whether a user can view another user’s blog page, when that other user doesn’t have any blog postings. The process is similar to the foregoing test. The developer logs onto the site using the drupalLogin method, passing the big_user as the user to log in as. The next step is to navigate to the own_user’s blog page using the drupalGet method. The developer checks to see whether the page loaded successfully using the assertResponse method, followed by a check to make sure the page title is properly displayed, using the assertTitle method. The text is properly displayed for a user’s blog page when that user doesn’t have any blog entries (using the assertText method).
/**
* View the blog of a user with no blog entries as another user. */
function testBlogPageNoEntries() { $this->drupalLogin($this->big_user);
$this->drupalGet('blog/' . $this->own_user->uid); $this->assertResponse(200);
$this->assertTitle(t("@name's blog", array('@name' => format_username($this->own_user)))
. ' | Drupal', t('Blog title was displayed'));
$this->assertText(t('@author has not created any blog entries.', array('@author' => format_username($this->own_user))), t('Users blog displayed with no entries'));
}
The doBasicTests function does just what its name implies, executes other test functions defined elsewhere in this test script. The function logs the user in and executes the tests that verify blogs, creates nodes, and verifies blog links.
/**
*Run basic tests on the indicated user.
*@param object $user
*The logged in user.
*@param boolean $admin
*User has 'access administration pages' privilege.
*/
private function doBasicTests($user, $admin) { // Login the user. $this->drupalLogin($user);
// Create blog node.
$node = $this->drupalCreateNode(array('type' => 'blog')); // Verify the user has access to all the blog nodes. $this->verifyBlogs($user, $node, $admin);
553
CHAPTER 25 ■ TESTING
//Create one more node to test the blog page with more than one node $this->drupalCreateNode(array('type' => 'blog', 'uid' => $user->uid));
//Verify the blog links are displayed.
$this->verifyBlogLinks($user);
}
The following functions continue to use the patterns just defined to perform various tests and verify and record results.
/**
*Verify the logged in user has the desired access to the various blog nodes.
*@param object $node_user
*The user who creates the node.
*@param object $node
*A node object.
*@param boolean $admin
*User has 'access administration pages' privilege.
*@param integer $response
*HTTP response code.
*/
private function verifyBlogs($node_user, $node, $admin, $response = 200) { $response2 = ($admin) ? 200 : 403;
// View blog help node. $this->drupalGet('admin/help/blog'); $this->assertResponse($response2); if ($response2 == 200) {
$this->assertTitle(t('Blog | Drupal'), t('Blog help node was displayed')); $this->assertText(t('Blog'), t('Blog help node was displayed'));
}
//Verify the blog block was displayed. $this->drupalGet(''); $this->assertResponse(200);
$this->assertText(t('Recent blog posts'), t('Blog block was displayed'));
//View blog node.
$this->drupalGet('node/' . $node->nid); $this->assertResponse(200);
$this->assertTitle($node->title . ' | Drupal', t('Blog node was displayed')); $breadcrumb = array(
l(t('Home'), NULL), l(t('Blogs'), 'blog'),
l(t("!name's blog", array('!name' => format_username($node_user))), 'blog/' . $node_user->uid),
);
$this->assertRaw(theme('breadcrumb', array('breadcrumb' => $breadcrumb)), t('Breadcrumbs were displayed'));
554
CHAPTER 25 ■ TESTING
// View blog edit node.
$this->drupalGet('node/' . $node->nid . '/edit'); $this->assertResponse($response);
if ($response == 200) {
$this->assertTitle('Edit Blog entry ' . $node->title . ' | Drupal', t('Blog edit node was displayed'));
}
if ($response == 200) { // Edit blog node. $edit = array();
$langcode = LANGUAGE_NONE; $edit["title"] = 'node/' . $node->nid;
$edit["body[$langcode][0][value]"] = $this->randomName(256); $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save')); $this->assertRaw(t('Blog entry %title has been updated.', array('%title' =>
$edit["title"])), t('Blog node was edited'));
// Delete blog node.
$this->drupalPost('node/' . $node->nid . '/delete', array(), t('Delete')); $this->assertResponse($response);
$this->assertRaw(t('Blog entry %title has been deleted.', array('%title' => $edit["title"])), t('Blog node was deleted'));
}
}
/**
*Verify the blog links are displayed to the logged in user.
*@param object $user
*The logged in user.
*/
private function verifyBlogLinks($user) {
// Confirm blog entries link exists on the user page. $this->drupalGet('user/' . $user->uid); $this->assertResponse(200);
$this->assertText(t('View recent blog entries'), t('View recent blog entries link was displayed'));
// Confirm the recent blog entries link goes to the user's blog page. $this->clickLink('View recent blog entries');
$this->assertTitle(t("@name's blog | Drupal", array('@name' => format_username($user))), t('View recent blog entries link target was correct'));
// Confirm a blog page was displayed. $this->drupalGet('blog'); $this->assertResponse(200);
$this->assertTitle('Blogs | Drupal', t('Blog page was displayed')); $this->assertText(t('Home'), t('Breadcrumbs were displayed')); $this->assertLink(t('Create new blog entry'));
555
