This tutorial will clarify some basic ideas about software testing. We'll give some strategies for testing and illustrate types of tests and when and why you'd use them. This document is written with Drupal in mind, but the concepts apply for other development environments you'll encounter as well. The tools will be different, but the ideas apply universally. By the end of this tutorial, you should understand what testing is for and how different types of tests support different purposes and outcomes.
In order to run functional tests that require JavaScript be executed for the feature to work, the tests need to be run in a browser that supports JavaScript. This is accomplished by using the WebDriver API in combination with an application like ChromeDriver or Selenium, which can remotely control a browser.
The exact setup for running functional tests is dependent on your development environment. We'll walk through a couple of common examples including using Docker (via DDEV) and stand-alone applications.
In this tutorial we'll:
- Learn how to install and run ChromeDriver and other necessary tools either in a Docker environment, or locally.
- Configure the relevant PHPUnit environment variables so they contain values appropriate for our specific environment.
- Execute Drupal's functional JavaScript tests via the
phpunitcommand.
By the end of this tutorial you should be able to install the applications required to run functional JavaScript tests in a browser, and know how to configure PHPUnit to make use of them.
Every theme can contain an optional THEMENAME.theme file. This file contains additional business logic written in PHP and is primarily used for manipulation of the variables available for a template file, and suggesting alternative candidate template file names. Themes can also use this file to implement some, but not all, of the hooks invoked by Drupal modules.
In this tutorial we'll learn:
- The use case for THEMENAME.theme files, and where to find them
- The different types of functions and hooks you can implement in a THEMENAME.theme file
By the end of this tutorial you should be able to know how to start adding PHP logic to your custom theme.
Themes and modules can alter the list of theme hook suggestions in order to add new ones, remove existing ones, or reorder the list. This powerful feature allows for the definition of custom logic in your application that can tell Drupal to use different templates based on your own unique needs. You might for example; use a different page template for authenticated users, or a custom block template for someone's birthday.
In this tutorial we'll cover:
- Adding new theme hook suggestions from a theme using
hook_theme_suggestions_HOOK_alter() - Altering the list of theme hook suggestions
- Removing theme hook suggestions
- Reordering the list of theme hook suggestions
Customizing the available regions in your theme is one of the first things you'll do when creating your own themes. Doing so gives you complete control over where content is displayed on the page, and the markup involved. Adding regions to a theme is a two-step process that involves editing your theme's THEMENAME.info.yml file and updating your page.html.twig file.
In this tutorial we'll:
- Declare one or more new regions in our themes THEMENAME.info.yml file.
- Output the content of those regions in our theme via the page.html.twig file.
By the end of this tutorial you should be able to add or edit the regions a theme provides. Also, you'll ensure that blocks placed into regions are displayed by outputting the regions in the page template.
Contributed and custom themes can ship with a thumbnail screenshot that will be used to represent the theme when listing themes in the administration UI.
In this tutorial you'll learn how to:
- Prepare your site for the screenshot
- Create a screenshot of your theme
- Define a screenshot file for your theme in the THEMENAME.info.yml file
In Drupal, whenever we output markup it's best practice to use a Twig template or a theme function. But whenever you need to output DOM elements within JavaScript the best practice is to use the Drupal.theme function. This function ensures that the output can be overridden just like the HTML output by Twig. This tutorial covers how to use the Drupal.theme function in your JavaScript when inserting DOM elements, as well as how to replace the markup output by other JavaScript code that is using the Drupal.theme function.
ESLint is the linting tool of choice for JavaScript in Drupal. In this tutorial we’ll show how to install the ESLint application and then use it to verify that your JavaScript files are meeting the Drupal coding standards.
Drupal (as of version 8.4) has adopted the Airbnb JavaScript coding standards. In this tutorial, we'll walk through how to install the necessary package dependencies to run eslint on JavaScript files within your Drupal site.
An asset library is a bundle of CSS and/or JavaScript files that work together to provide a style and functionality for a specific component. They are frequently used to isolate the functionality and styling of a specific component, like the tabs displayed at the top of each node, into a reusable library. If you want to include CSS and/or JavaScript in your Drupal theme or module you'll need to declare an asset library that tells Drupal about the existence, and location, of those files. And then attach that library to a page, or specific element, so that it gets loaded when needed.
In this tutorial we’ll:
- Define what an asset library is.
- Explain why asset libraries are used to include JavaScript and CSS files.
- Look at some example asset library definitions.
By the end of this tutorial you should be able to define what asset libraries are, and when you'll need to create one.
Preprocess functions allow Drupal themes to manipulate the variables that are used in Twig template files by using PHP functions to preprocess data before it is exposed to each template. All of the dynamic content available to theme developers within a Twig template file is exposed through a preprocess function. Understanding how preprocess functions work, and the role they play, is important for both module developers and theme developers.
In this tutorial we'll learn:
- What preprocess functions are and how they work
- The use case for preprocess functions
- The order of execution for preprocess functions
By the end of this tutorial you should be able to explain what preprocess functions are and the role they play in a Drupal theme.
Template files are responsible for the HTML markup of every page generated by Drupal. Any file ending with the .html.twig extension is a template file. These files are composed of standard HTML markup as well as tokens used by the Twig template engine to represent dynamic content that will be substituted into the HTML markup when the template is used. As a theme developer, you'll work with this a lot.
In this tutorial we’re going to learn about:
- What template files are, and how they fit into the big picture of creating a theme
- How template files are used in order to allow theme developers to modify the HTML markup output by Drupal
- Naming conventions for, and specificity of, template files
What Is a Theme?
FreeThemes are the part of Drupal that you, and anyone else visiting your Drupal powered application, see when they view any page in their browser. You can think of a theme as a layer, kind of like a screen, that exists between your Drupal content and the users of your site. Whenever a page is requested Drupal does the work of assembling the content to display into structured data which is then handed off to the presentation layer to determine how to visually represent the data provided.
Drupal themes are created by front-end developer. Frequently referred to as themers, or theme developers. Themes consist of standard web assets like CSS, JavaScript, and images, combined with Drupal-specific templates for generating HTML markup, and YAML files for telling Drupal about the file and features that make up each individual theme.
In this tutorial we'll:
- Explain what a Drupal theme is.
- Explain the role of a Drupal themer in the process of building a Drupal site.
- Get a high level overview of the types of files/code that themes are made of.
By the end of this tutorial you should be able to explain what a Drupal theme is, and the kind of work a Drupal theme developer will be expected to do.
Maybe you've heard of anonymous closures but you're not quite sure how they apply in Drupal, or why using them is considered a best-practice. Anonymous closures allow you to avoid accidentally clashing with anything in the global scope, as well as to alias the jQuery object to the more commonly used $. This is necessary because Drupal runs jQuery in no-conflict mode. This tutorial will look at the syntax used for placing your custom JavaScript code inside an anonymous closure, and why it's a good idea to do so.
In this tutorial we'll:
- Explain what a closure is (briefly), and what immediately invoked function expressions are
- Show how typically Drupal JavaScript gets wrapped in a closure
- Provide a copy/paste example you can use in your own code
By the end of this tutorial you should be able to explain what an anonymous closure is, and how to use one in your custom JavaScript for Drupal.
Like most output in Drupal, Views relies on Twig templates for a significant amount of its rendering. In this tutorial we'll identify where you can find the default Views templates within your file system, what the common templates are for, and how to name your templates so that they are applied to specific views.
By the end of this tutorial, you should be able to:
- Identify where to find default views templates
- Understand which templates apply to what part of a view
- Get a sense of the template suggestions and how to use them to limit where your custom templates are applied
- Identify a view's machine name
- Identify a display's machine name
- Identify a field's machine name
Now that we understand what templates are and how we can use them, let's override some templates! In this tutorial we'll copy the views wrapper template to our theme and override it so that we can customize the markup for the Baseball Players view. Then we'll modify the template so that our view's pager appears both above and below our table of players.
When a user creates a view, they can set access rules, and restrict who can see the view. The core Views module allows you to limit access by a user's permissions, or roles. If your project requires custom access rules you can define a custom Views access plugin. This will allow you to determine a user's access based on any custom logic you might have.
In this tutorial we'll:
- Define a custom Views access plugin that determines access based on data stored in a field on the user entity type.
- Learn the difference between access checks for routes and access checks for Views displays, and how to use each of them.
- Use our new custom access handler when defining a view.
By the end of this tutorial you should know how to define a custom Views access plugin and use it to grant access to a view.
Area handler plugins are used to determine what is displayed in areas such as the header, footer, and empty text sections of a view. The handlers provided by core cover displaying result summaries, and allowing users to enter in HTML to display. They are already pretty versatile. However, they may require code knowledge to accomplish some specific tasks. If you'd like to have a more specialized area handler that can be reused across multiple views, you may want to look into defining a custom area handler plugin.
In this tutorial we'll:
- Create a custom area handler plugin with settings that can be updated through the Views UI.
- Use the new area handler plugin in a view.
By the end of this tutorial you should know how to define a custom area handler with settings and use it in Views.
In Expose a Custom Database Table to Views we learned how to let Views know about custom tables created by a Drupal module. In that example, the custom table was a stand-alone one, without any connections to the other tables in the database. However, it's common for data in one table to relate to data in another.
For example, you might have TableA with the columns first_name, last_name, email and TableB with the columns email, score. TableA.email and TableB.email can be used to join the two tables together.
It's useful to define these relationships for Views so that when TableA is used as the base table the fields from TableB are also available in the view. When the fields from a related table are automatically loaded, this is known as an implicit relationship. Our earlier example could benefit from the relationship with the users_field_data. This relationship will allow us to associate First Name and Last Name fields from the subscriptions table with the users on the site.
In this tutorial we'll:
- Define the difference between implicit and explicit relationships in Views.
- Learn how to create an implicit relationship between 2 tables using
hook_views_data.
By the end of this tutorial you should know how to describe custom implicit relationships in a view, making data from one or more secondary tables available to the Views query builder.
Any Drupal module that provides custom database tables should implement hook_views_data() to describe the schema of those tables to Views. This hook is used to provide information to Views about the fields in a table, their human-readable names, and the types of data (string, integer, date, etc.) stored in each column. You can also tell Views how it should handle sorting, filtering, and formatting the data. Implementations of hook_views_data() can also be used to describe relationships between tables.
If you're creating a module that implements hook_schema() and adds new tables to the database it's a good idea to also add support for Views. Among other things, it'll allow administrators to create any user-facing displays of data from your table using Views. Then, they can be edited without having to write code. Once you've described your table to Views via hook_views_data() Views will be able to provide a way for administrators to construct queries against your data via the UI.
In this tutorial we'll:
- Use
hook_views_data()to expose a custom table defined in a Drupal module to Views. - Learn how to describe different types of data to Views.
- Demonstrate the relationship between
hook_views_data()and what a site administrator has access to in the Views UI.
By the end of this tutorial you should know how to describe custom database tables and their fields to the Views module.
Using hook_views_post_render() Drupal module developers can change the render array representation of a View before it gets displayed. You can use this to modify the computed HTML of a view just before it gets printed. This hook is similar to using a preprocess function for a template file, but in this case you have access to the whole render tree, not just the specific leaf covered by the template file you're preprocessing.
This hooks gets called after the Views object has been through the render process, and all the different configured displays and formatters have been applied, producing a render array representation of the results of the view. This hook receives an $output parameter which contains the render array, and only changes made directly to the $ouput variable will have any effect. This hook is the last chance to make changes to the render array before it is converted to markup.
You can use hook_views_post_render() to change #cache properties for the View, and to do things like add CSS classes to rows, and change the page title.
In this tutorial we'll:
- Learn how to alter a view's rendered output right after the Views object completes the render phase of the build cycle.
- Use
hook_views_post_render()to change the title of the view's display
By the end of this tutorial you should know how to alter the render array generated by a View prior to it being passed to the theme layer and converted to HTML.