Compare commits
No commits in common. "main" and "start" have entirely different histories.
12 changed files with 33 additions and 374 deletions
composer.jsoncomposer.lock
web/modules/custom
example/tests/src/Functional
my_module
|
@ -109,7 +109,6 @@
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"drupal/core-dev": "^10",
|
"drupal/core-dev": "^10",
|
||||||
"fenetikm/autoload-drupal": "dev-autoload-tests",
|
"fenetikm/autoload-drupal": "dev-autoload-tests",
|
||||||
"phpspec/prophecy-phpunit": "^2",
|
"phpspec/prophecy-phpunit": "^2"
|
||||||
"phpstan/phpstan-strict-rules": "^1.5"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
51
composer.lock
generated
51
composer.lock
generated
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "2d3f22a75cc98e2996a5d6b9f3934faf",
|
"content-hash": "b7efed8e16e9b99d9aba5afbdb499669",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "asm89/stack-cors",
|
"name": "asm89/stack-cors",
|
||||||
|
@ -8218,55 +8218,6 @@
|
||||||
},
|
},
|
||||||
"time": "2023-05-26T11:05:59+00:00"
|
"time": "2023-05-26T11:05:59+00:00"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "phpstan/phpstan-strict-rules",
|
|
||||||
"version": "1.5.1",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
|
|
||||||
"reference": "b21c03d4f6f3a446e4311155f4be9d65048218e6"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/b21c03d4f6f3a446e4311155f4be9d65048218e6",
|
|
||||||
"reference": "b21c03d4f6f3a446e4311155f4be9d65048218e6",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": "^7.2 || ^8.0",
|
|
||||||
"phpstan/phpstan": "^1.10"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"nikic/php-parser": "^4.13.0",
|
|
||||||
"php-parallel-lint/php-parallel-lint": "^1.2",
|
|
||||||
"phpstan/phpstan-deprecation-rules": "^1.1",
|
|
||||||
"phpstan/phpstan-phpunit": "^1.0",
|
|
||||||
"phpunit/phpunit": "^9.5"
|
|
||||||
},
|
|
||||||
"type": "phpstan-extension",
|
|
||||||
"extra": {
|
|
||||||
"phpstan": {
|
|
||||||
"includes": [
|
|
||||||
"rules.neon"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"PHPStan\\": "src/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"description": "Extra strict and opinionated rules for PHPStan",
|
|
||||||
"support": {
|
|
||||||
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
|
|
||||||
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.5.1"
|
|
||||||
},
|
|
||||||
"time": "2023-03-29T14:47:40+00:00"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "phpunit/php-code-coverage",
|
"name": "phpunit/php-code-coverage",
|
||||||
"version": "9.2.27",
|
"version": "9.2.27",
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Drupal\Tests\example\Functional;
|
|
||||||
|
|
||||||
use Drupal\Tests\BrowserTestBase;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
|
|
||||||
final class AdminPageTest extends BrowserTestBase {
|
|
||||||
|
|
||||||
public $defaultTheme = 'stark';
|
|
||||||
|
|
||||||
/** @test */
|
|
||||||
public function the_admin_page_is_not_accessible_to_anonymous_users(): void {
|
|
||||||
$this->drupalGet(path: '/admin');
|
|
||||||
|
|
||||||
$assert = $this->assertSession();
|
|
||||||
|
|
||||||
$assert->statusCodeEquals(Response::HTTP_FORBIDDEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @test */
|
|
||||||
public function the_admin_page_is_accessible_by_admin_users(): void {
|
|
||||||
$adminUser = $this->createUser(
|
|
||||||
permissions: [
|
|
||||||
'access administration pages',
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->drupalLogin(account: $adminUser);
|
|
||||||
|
|
||||||
$this->drupalGet(path: '/admin');
|
|
||||||
|
|
||||||
$assert = $this->assertSession();
|
|
||||||
|
|
||||||
$assert->statusCodeEquals(Response::HTTP_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\Tests\example\Functional;
|
||||||
|
|
||||||
|
use Drupal\Tests\BrowserTestBase;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
final class ExamplePageTest extends BrowserTestBase {
|
||||||
|
|
||||||
|
public $defaultTheme = 'stark';
|
||||||
|
|
||||||
|
protected static $modules = [
|
||||||
|
// Core.
|
||||||
|
'node',
|
||||||
|
|
||||||
|
// Custom.
|
||||||
|
"example"
|
||||||
|
];
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function should_load_the_example_page_for_anonymous_users(): void {
|
||||||
|
// Arrange.
|
||||||
|
|
||||||
|
// Act.
|
||||||
|
$this->drupalGet('/@opdavies/drupal-module-template');
|
||||||
|
|
||||||
|
// Assert.
|
||||||
|
$this->assertSession()->statusCodeEquals(Response::HTTP_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,30 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Drupal\Tests\example\Functional;
|
|
||||||
|
|
||||||
use Drupal\Tests\BrowserTestBase;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
|
|
||||||
final class FrontPageTest extends BrowserTestBase {
|
|
||||||
|
|
||||||
public $defaultTheme = 'stark';
|
|
||||||
|
|
||||||
protected static $modules = ['node', 'views'];
|
|
||||||
|
|
||||||
/** @test */
|
|
||||||
public function the_front_page_loads_for_anonymous_users(): void {
|
|
||||||
$this->config('system.site')
|
|
||||||
->set('page.front', '/node')
|
|
||||||
->save(TRUE);
|
|
||||||
|
|
||||||
$this->drupalGet('<front>');
|
|
||||||
|
|
||||||
$assert = $this->assertSession();
|
|
||||||
|
|
||||||
$assert->statusCodeEquals(Response::HTTP_OK);
|
|
||||||
$assert->pageTextContains('No front page content has been created yet.');
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
name: My Module
|
|
||||||
type: module
|
|
||||||
core_version_requirement: ^10
|
|
|
@ -1,7 +0,0 @@
|
||||||
blog.page:
|
|
||||||
path: /blog
|
|
||||||
defaults:
|
|
||||||
_controller: Drupal\my_module\Controller\BlogPageController
|
|
||||||
_title: Blog
|
|
||||||
requirements:
|
|
||||||
_permission: access content
|
|
|
@ -1,6 +0,0 @@
|
||||||
services:
|
|
||||||
Drupal\my_module\Controller\BlogPageController:
|
|
||||||
autowire: true
|
|
||||||
|
|
||||||
Drupal\my_module\Repository\ArticleRepository:
|
|
||||||
autowire: true
|
|
|
@ -1,51 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Drupal\my_module\Controller;
|
|
||||||
|
|
||||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
|
||||||
use Drupal\Core\Entity\EntityViewBuilderInterface;
|
|
||||||
use Drupal\Core\Render\RendererInterface;
|
|
||||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
|
||||||
use Drupal\my_module\Repository\ArticleRepository;
|
|
||||||
|
|
||||||
final class BlogPageController {
|
|
||||||
|
|
||||||
use StringTranslationTrait;
|
|
||||||
|
|
||||||
private EntityViewBuilderInterface $nodeViewBuilder;
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
private RendererInterface $renderer,
|
|
||||||
EntityTypeManagerInterface $entityTypeManager,
|
|
||||||
private ArticleRepository $articleRepository,
|
|
||||||
) {
|
|
||||||
$this->nodeViewBuilder = $entityTypeManager->getViewBuilder(entity_type_id: 'node');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function __invoke(): array {
|
|
||||||
$articles = $this->articleRepository->getAll();
|
|
||||||
|
|
||||||
if ($articles === []) {
|
|
||||||
return ['#markup' => $this->t('Welcome to my blog!')];
|
|
||||||
}
|
|
||||||
|
|
||||||
$build = [];
|
|
||||||
|
|
||||||
foreach ($articles as $article) {
|
|
||||||
$build[] = $this->nodeViewBuilder->view(
|
|
||||||
entity: $article,
|
|
||||||
view_mode: 'teaser',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'#markup' => $this->renderer->render($build),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Drupal\my_module\Repository;
|
|
||||||
|
|
||||||
use Drupal\Core\Entity\EntityStorageInterface;
|
|
||||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
|
||||||
use Drupal\node\NodeInterface;
|
|
||||||
|
|
||||||
final class ArticleRepository {
|
|
||||||
|
|
||||||
private EntityStorageInterface $nodeStorage;
|
|
||||||
|
|
||||||
public function __construct(EntityTypeManagerInterface $entityTypeManager) {
|
|
||||||
$this->nodeStorage = $entityTypeManager->getStorage(entity_type_id: 'node');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<int, NodeInterface>
|
|
||||||
*/
|
|
||||||
public function getAll(): array {
|
|
||||||
/** @var array<int, NodeInterface> */
|
|
||||||
$articles = $this->nodeStorage->loadByProperties([
|
|
||||||
'status' => NodeInterface::PUBLISHED,
|
|
||||||
'type' => 'article',
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Sort the articles by their created time.
|
|
||||||
uasort($articles, function (NodeInterface $a, NodeInterface $b): int {
|
|
||||||
return $a->getCreatedTime() < $b->getCreatedTime() ? 1 : -1;
|
|
||||||
});
|
|
||||||
|
|
||||||
return $articles;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Drupal\Tests\my_module\Functional;
|
|
||||||
|
|
||||||
use Drupal\Tests\BrowserTestBase;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
|
|
||||||
final class BlogPageTest extends BrowserTestBase {
|
|
||||||
|
|
||||||
public $defaultTheme = 'stark';
|
|
||||||
|
|
||||||
protected static $modules = [
|
|
||||||
// Core.
|
|
||||||
'node',
|
|
||||||
|
|
||||||
// Custom.
|
|
||||||
'my_module',
|
|
||||||
];
|
|
||||||
|
|
||||||
/** @test */
|
|
||||||
public function the_blog_page_loads_for_anonymous_users_and_contains_the_right_text(): void {
|
|
||||||
$this->drupalGet('/blog');
|
|
||||||
|
|
||||||
$assert = $this->assertSession();
|
|
||||||
|
|
||||||
$assert->statusCodeEquals(Response::HTTP_OK);
|
|
||||||
$assert->responseContains('<h1>Blog</h1>');
|
|
||||||
$assert->pageTextContains('Welcome to my blog!');
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @test */
|
|
||||||
public function it_shows_articles(): void {
|
|
||||||
$this->createContentType(['type' => 'article']);
|
|
||||||
|
|
||||||
$this->createNode([
|
|
||||||
'title' => 'This is a test article',
|
|
||||||
'type' => 'article',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->drupalGet('/blog');
|
|
||||||
|
|
||||||
$assert = $this->assertSession();
|
|
||||||
|
|
||||||
$assert->statusCodeEquals(Response::HTTP_OK);
|
|
||||||
$assert->pageTextContains('This is a test article');
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,98 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Drupal\Tests\my_module\Kernel\Repository;
|
|
||||||
|
|
||||||
use Drupal\Core\Datetime\DrupalDateTime;
|
|
||||||
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
|
|
||||||
use Drupal\my_module\Repository\ArticleRepository;
|
|
||||||
use Drupal\node\Entity\Node;
|
|
||||||
use Drupal\Tests\node\Traits\NodeCreationTrait;
|
|
||||||
|
|
||||||
final class ArticleRepositoryTest extends EntityKernelTestBase {
|
|
||||||
|
|
||||||
use NodeCreationTrait;
|
|
||||||
|
|
||||||
protected $strictConfigSchema = FALSE;
|
|
||||||
|
|
||||||
public static $modules = [
|
|
||||||
'node',
|
|
||||||
'my_module',
|
|
||||||
];
|
|
||||||
|
|
||||||
protected function setUp(): void {
|
|
||||||
parent::setUp();
|
|
||||||
|
|
||||||
$this->installSchema(module: 'node', tables: ['node_access']);
|
|
||||||
|
|
||||||
$this->installConfig([
|
|
||||||
'filter',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @test */
|
|
||||||
public function nodes_that_are_not_articles_are_not_returned() {
|
|
||||||
$this->createNode(['type' => 'article'])->save();
|
|
||||||
$this->createNode(['type' => 'page'])->save();
|
|
||||||
$this->createNode(['type' => 'article'])->save();
|
|
||||||
$this->createNode(['type' => 'page'])->save();
|
|
||||||
$this->createNode(['type' => 'article'])->save();
|
|
||||||
|
|
||||||
$this->assertCount(5, Node::loadMultiple());
|
|
||||||
|
|
||||||
$repository = $this->container->get(ArticleRepository::class);
|
|
||||||
$articles = $repository->getAll();
|
|
||||||
|
|
||||||
$this->assertCount(3, $articles);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @test */
|
|
||||||
public function only_published_articles_are_returned() {
|
|
||||||
$this->createNode(['type' => 'article', 'status' => Node::PUBLISHED])->save();
|
|
||||||
$this->createNode(['type' => 'article', 'status' => Node::NOT_PUBLISHED])->save();
|
|
||||||
$this->createNode(['type' => 'article', 'status' => Node::PUBLISHED])->save();
|
|
||||||
$this->createNode(['type' => 'article', 'status' => Node::NOT_PUBLISHED])->save();
|
|
||||||
$this->createNode(['type' => 'article', 'status' => Node::PUBLISHED])->save();
|
|
||||||
|
|
||||||
$repository = $this->container->get(ArticleRepository::class);
|
|
||||||
$articles = $repository->getAll();
|
|
||||||
|
|
||||||
$this->assertCount(3, $articles);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @test */
|
|
||||||
public function nodes_are_ordered_by_date_and_returned_newest_first() {
|
|
||||||
$this->createNode([
|
|
||||||
'type' => 'article',
|
|
||||||
'created' => (new DrupalDateTime('-2 days'))->getTimestamp(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->createNode([
|
|
||||||
'type' => 'article',
|
|
||||||
'created' => (new DrupalDateTime('-1 week'))->getTimestamp(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->createNode([
|
|
||||||
'type' => 'article',
|
|
||||||
'created' => (new DrupalDateTime('-1 hour'))->getTimestamp(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->createNode([
|
|
||||||
'type' => 'article',
|
|
||||||
'created' => (new DrupalDateTime('-1 year'))->getTimestamp(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->createNode([
|
|
||||||
'type' => 'article',
|
|
||||||
'created' => (new DrupalDateTime('-1 month'))->getTimestamp(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
$repository = $this->container->get(ArticleRepository::class);
|
|
||||||
$nodes = $repository->getAll();
|
|
||||||
$nodeIds = array_keys($nodes);
|
|
||||||
|
|
||||||
$this->assertSame([3, 1, 2, 5, 4], $nodeIds);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Reference in a new issue