Compare commits

..

No commits in common. "8.x-1.x" and "1-fail" have entirely different histories.

8 changed files with 25 additions and 383 deletions

1
.gitignore vendored
View file

@ -1 +0,0 @@
composer.lock

View file

@ -1,31 +1,16 @@
# TDD Example Drupal 8 Blog Module
# TDD Dublin demo module
A demo module to accompany my [TDD - Test Driven Drupal][0] talk, originally for DrupalCamp
A demo module to accompany my [TDD Test Driven Drupal][0] talk at DrupalCamp
Dublin 2017.
In order to see my workflow of writing comments first, converting them into
failing tests, and then writing the implementation code to make them pass, you
can see the [list of previous commits][1] and see each step taken, as well as
[the tags][2] that identify the commits when each failing test is added and
then subsequently passes.
## Acceptance Criteria
This module will be used to demonstrate how to take a test-driven approach to
develop a module to the following acceptance criteria:
- As a site visitor
- I want to see a list of all published blog posts at `/blog`
- Ordered by post date, with the newest posts first
## Installation
Within your Drupal 8 site:
```bash
cd modules
git clone git@github.com:opdavies/drupal-module-tdd-blog.git tdd_blog
```
- I want to see a list of all published pages at `/pages`
- Ordered alphabetically by title
## Running the Tests
@ -33,23 +18,23 @@ These tests are functional tests based on the `BrowserTestBase` class so need
to be executed with PHPUnit (which is required in core's `composer.json` file).
The path to your `vendor` directory may be different depending on your setup.
Because of autoloading, you will either need to be inside Drupal's `core` subdirectory
, or add `-c core` to the PHPUnit command when running the tests for them to execute successfully.
Because of autoloading, you will need to be inside Drupal's `core` subdirectory
when running the tests for them to execute successfully.
This also assumes that the module is within a `modules/custom` directory and
named `tdd_blog` as per the repository name.
named `tdd_dublin` as per the repository name.
```
vendor/bin/phpunit -c core modules/custom/tdd_blog
cd core
../vendor/bin/phpunit ../modules/custom/tdd_dublin
```
You can use PHPUnit's `--filter` option to specify a single test method to run,
rather than all of the tests within the module. For example:
```
vendor/bin/phpunit -c core modules/custom/tdd_blog --filter=testOnlyPublishedPagesAreShown
../vendor/bin/phpunit ../modules/custom/tdd_dublin --filter=testOnlyPublishedPagesAreShown
```
[0]: https://www.oliverdavies.uk/talks/tdd-test-driven-drupal
[1]: https://github.com/opdavies/drupal-module-tdd-blog/commits/HEAD
[2]: https://github.com/opdavies/drupal-module-tdd-blog/tags

View file

@ -1,4 +0,0 @@
{
"name": "drupal/tdd_blog",
"type": "drupal-custom-module"
}

View file

@ -1,10 +0,0 @@
langcode: en
status: true
dependencies: { }
name: Article
type: article
description: 'Use <em>articles</em> for time-sensitive content like news, press releases or blog posts.'
help: ''
new_revision: true
preview_mode: 1
display_submitted: true

View file

@ -1,228 +0,0 @@
langcode: en
status: true
dependencies:
config:
- node.type.article
module:
- node
- user
id: blog
label: Blog
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
core: 8.x
display:
default:
display_plugin: default
id: default
display_title: Master
position: 0
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: mini
options:
items_per_page: 10
offset: 0
id: 0
total_pages: null
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 25, 50'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
tags:
previous:
next:
style:
type: default
options:
grouping: { }
row_class: ''
default_row_class: true
uses_fields: false
row:
type: fields
options:
inline: { }
separator: ''
hide_empty: false
default_field_elements: true
fields:
title:
id: title
table: node_field_data
field: title
entity_type: node
entity_field: title
label: ''
alter:
alter_text: false
make_link: false
absolute: false
trim: false
word_boundary: false
ellipsis: false
strip_tags: false
html: false
hide_empty: false
empty_zero: false
settings:
link_to_entity: true
plugin_id: field
relationship: none
group_type: group
admin_label: ''
exclude: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_alter_empty: true
click_sort_column: value
type: string
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
filters:
status:
value: '1'
table: node_field_data
field: status
plugin_id: boolean
entity_type: node
entity_field: status
id: status
expose:
operator: ''
group: 1
type:
id: type
table: node_field_data
field: type
relationship: none
group_type: group
admin_label: ''
operator: in
value:
article: article
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
reduce: false
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
entity_type: node
entity_field: type
plugin_id: bundle
sorts:
created:
id: created
table: node_field_data
field: created
relationship: none
group_type: group
admin_label: ''
order: ASC
exposed: false
expose:
label: ''
granularity: second
entity_type: node
entity_field: created
plugin_id: date
header: { }
footer: { }
empty: { }
relationships: { }
arguments: { }
display_extenders: { }
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url.query_args
- 'user.node_grants:view'
- user.permissions
tags: { }
page_1:
display_plugin: page
id: page_1
display_title: Page
position: 1
display_options:
display_extenders: { }
path: blog
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url.query_args
- 'user.node_grants:view'
- user.permissions
tags: { }

View file

@ -1,9 +1,4 @@
name: 'TDD Blog'
name: 'TDD Dublin'
description: 'A demo module for DrupalCamp Dublin to show test driven module development.'
core: 8.x
core_version_requirement: ^8 || ^9
type: module
dependencies:
- drupal:node
- drupal:views

View file

@ -1,20 +1,24 @@
<?php
namespace Drupal\Tests\tdd_blog\Functional;
namespace Drupal\Tests\tdd_dublin\Functional;
use Drupal\Tests\BrowserTestBase;
use Symfony\Component\HttpFoundation\Response;
class PageListTest extends BrowserTestBase {
protected static $modules = ['tdd_blog'];
/**
* {@inheritdoc}
*/
protected static $modules = ['tdd_dublin'];
protected $defaultTheme = 'stark';
public function testBlogPageExists() {
$this->drupalGet('blog');
$this->assertSession()->statusCodeEquals(Response::HTTP_OK);
/**
* Test that the pages listing page exists and is accessible.
*/
public function testListingPageExists() {
// Go to /pages and check that it is accessible by checking the status
// code.
$this->drupalGet('pages');
$this->assertSession()->statusCodeEquals(200);
}
}

View file

@ -1,99 +0,0 @@
<?php
namespace Drupal\Tests\tdd_blog\Kernel;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
use Drupal\Tests\node\Traits\NodeCreationTrait;
use Drupal\views\ResultRow;
/**
* @group tdd_blog
*/
class PageListTest extends EntityKernelTestBase {
use NodeCreationTrait;
/**
* {@inheritdoc}
*/
public static $modules = [
'node',
'tdd_blog',
'views',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('node');
$this->installEntitySchema('user');
$this->installConfig(['filter', 'tdd_blog']);
}
/**
* Ensure that only the correct nodes are returned.
*
* Ensure that only published pages are returned by the view. Unpublished
* pages or content of different types should not be shown.
*/
public function testOnlyPublishedArticlesAreShown() {
// This is a published article, so it should be visible.
$this->createNode(['type' => 'page', 'status' => TRUE]);
// This is a page, so it should not be visible.
$this->createNode(['type' => 'article']);
// This article is not published, so it should not be visible.
$this->createNode(['type' => 'article', 'status' => FALSE]);
// Rather than testing the rendered HTML, we are going to load the view
// results programmatically and run assertions against the data it returns.
// This makes it easier to test certain scenarios, and ensures that the
// test is future-proofed and won't fail at a later date due to a change in
// the presentation code.
$nids = $this->getViewResults();
// Only node 1 matches the criteria of being a published page, so only that
// node ID should be being returned from the view. assertEquals() can be
// used to compare the expected result to what is being returned.
$this->assertEquals([2], $nids);
}
/**
* Ensure that the results are ordered by title.
*/
public function testArticlesAreOrderedByDate() {
$this->createNode(['type' => 'article', 'created' => (new DrupalDateTime('+1 day'))->getTimestamp()]);
$this->createNode(['type' => 'article', 'created' => (new DrupalDateTime('+1 month'))->getTimestamp()]);
$this->createNode(['type' => 'article', 'created' => (new DrupalDateTime('+3 days'))->getTimestamp()]);
$this->createNode(['type' => 'article', 'created' => (new DrupalDateTime('+1 hour'))->getTimestamp()]);
// Get the result data from the view.
$nids = $this->getViewResults();
// Compare the expected order based on the titles defined above to the
// ordered results from the view.
$this->assertEquals([4, 1, 3, 2], $nids);
}
/**
* Load the view and get the results.
*
* @param string $view
* (optional) The name of the view. Defaults to 'blog'.
*
* @return array
* An array of returned entity IDs.
*/
private function getViewResults($view = 'blog') {
return array_map(function (ResultRow $result) {
return $result->_entity->id();
}, views_get_view_result($view));
}
}