@electriccitizen/bolt 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +361 -0
- package/dist/adapters/ddev.d.ts +16 -0
- package/dist/adapters/ddev.js +75 -0
- package/dist/adapters/ddev.js.map +1 -0
- package/dist/adapters/index.d.ts +1 -0
- package/dist/adapters/index.js +2 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +167 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/doctor.d.ts +4 -0
- package/dist/commands/doctor.js +263 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts +12 -0
- package/dist/commands/init.js +319 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/pr.d.ts +20 -0
- package/dist/commands/pr.js +282 -0
- package/dist/commands/pr.js.map +1 -0
- package/dist/commands/refresh.d.ts +22 -0
- package/dist/commands/refresh.js +375 -0
- package/dist/commands/refresh.js.map +1 -0
- package/dist/commands/suppress.d.ts +10 -0
- package/dist/commands/suppress.js +86 -0
- package/dist/commands/suppress.js.map +1 -0
- package/dist/commands/test.d.ts +5 -0
- package/dist/commands/test.js +106 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/commands/update.d.ts +26 -0
- package/dist/commands/update.js +573 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/config.d.ts +47 -0
- package/dist/config.js +187 -0
- package/dist/config.js.map +1 -0
- package/dist/formatters/index.d.ts +2 -0
- package/dist/formatters/index.js +3 -0
- package/dist/formatters/index.js.map +1 -0
- package/dist/formatters/json.d.ts +5 -0
- package/dist/formatters/json.js +7 -0
- package/dist/formatters/json.js.map +1 -0
- package/dist/formatters/markdown.d.ts +5 -0
- package/dist/formatters/markdown.js +144 -0
- package/dist/formatters/markdown.js.map +1 -0
- package/dist/formatters/text.d.ts +5 -0
- package/dist/formatters/text.js +123 -0
- package/dist/formatters/text.js.map +1 -0
- package/dist/plugins/accessibility.d.ts +5 -0
- package/dist/plugins/accessibility.js +116 -0
- package/dist/plugins/accessibility.js.map +1 -0
- package/dist/plugins/browser-smoke.d.ts +6 -0
- package/dist/plugins/browser-smoke.js +331 -0
- package/dist/plugins/browser-smoke.js.map +1 -0
- package/dist/plugins/field-interaction.d.ts +6 -0
- package/dist/plugins/field-interaction.js +570 -0
- package/dist/plugins/field-interaction.js.map +1 -0
- package/dist/plugins/index.d.ts +6 -0
- package/dist/plugins/index.js +28 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/linkit.d.ts +8 -0
- package/dist/plugins/linkit.js +170 -0
- package/dist/plugins/linkit.js.map +1 -0
- package/dist/plugins/media-browser.d.ts +6 -0
- package/dist/plugins/media-browser.js +257 -0
- package/dist/plugins/media-browser.js.map +1 -0
- package/dist/plugins/structural-smoke.d.ts +6 -0
- package/dist/plugins/structural-smoke.js +90 -0
- package/dist/plugins/structural-smoke.js.map +1 -0
- package/dist/plugins/visual-regression.d.ts +8 -0
- package/dist/plugins/visual-regression.js +214 -0
- package/dist/plugins/visual-regression.js.map +1 -0
- package/dist/plugins/wysiwyg.d.ts +8 -0
- package/dist/plugins/wysiwyg.js +221 -0
- package/dist/plugins/wysiwyg.js.map +1 -0
- package/dist/runner.d.ts +21 -0
- package/dist/runner.js +293 -0
- package/dist/runner.js.map +1 -0
- package/dist/suppression.d.ts +55 -0
- package/dist/suppression.js +223 -0
- package/dist/suppression.js.map +1 -0
- package/dist/types.d.ts +178 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/modules/bolt_inspect/bolt_inspect.info.yml +6 -0
- package/modules/bolt_inspect/bolt_inspect.services.yml +22 -0
- package/modules/bolt_inspect/composer.json +16 -0
- package/modules/bolt_inspect/drush.services.yml +10 -0
- package/modules/bolt_inspect/src/Drush/Commands/BoltInspectCommands.php +203 -0
- package/modules/bolt_inspect/src/Service/ContentGenerator.php +586 -0
- package/modules/bolt_inspect/src/Service/SiteProfiler.php +362 -0
- package/modules/bolt_inspect/src/Service/TestEntityTracker.php +98 -0
- package/package.json +46 -0
- package/scripts/setup.sh +34 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared interfaces for the Bolt test runner.
|
|
3
|
+
*/
|
|
4
|
+
import type { Browser, BrowserContext } from 'playwright';
|
|
5
|
+
export interface FieldDefinition {
|
|
6
|
+
name: string;
|
|
7
|
+
label: string;
|
|
8
|
+
type: string;
|
|
9
|
+
required: boolean;
|
|
10
|
+
cardinality: number;
|
|
11
|
+
settings: Record<string, unknown>;
|
|
12
|
+
}
|
|
13
|
+
export interface ContentTypeDefinition {
|
|
14
|
+
id: string;
|
|
15
|
+
label: string;
|
|
16
|
+
fields: FieldDefinition[];
|
|
17
|
+
}
|
|
18
|
+
export interface ParagraphBundle {
|
|
19
|
+
id: string;
|
|
20
|
+
label: string;
|
|
21
|
+
fields: FieldDefinition[];
|
|
22
|
+
}
|
|
23
|
+
export interface MenuItem {
|
|
24
|
+
title: string;
|
|
25
|
+
url: string;
|
|
26
|
+
}
|
|
27
|
+
export interface MenuDefinition {
|
|
28
|
+
name: string;
|
|
29
|
+
items: MenuItem[];
|
|
30
|
+
}
|
|
31
|
+
export interface RepresentativeUrl {
|
|
32
|
+
url: string;
|
|
33
|
+
source: 'homepage' | 'menu' | 'content_type' | 'view' | 'custom';
|
|
34
|
+
label: string;
|
|
35
|
+
contentType?: string;
|
|
36
|
+
viewId?: string;
|
|
37
|
+
displayId?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface SiteProfile {
|
|
40
|
+
boltInspectVersion?: string;
|
|
41
|
+
contentTypes: ContentTypeDefinition[];
|
|
42
|
+
paragraphBundles: ParagraphBundle[];
|
|
43
|
+
enabledModules: string[];
|
|
44
|
+
routes: string[];
|
|
45
|
+
mediaTypes: string[];
|
|
46
|
+
menus: MenuDefinition[];
|
|
47
|
+
representativeUrls: RepresentativeUrl[];
|
|
48
|
+
}
|
|
49
|
+
export interface TestResult {
|
|
50
|
+
plugin: string;
|
|
51
|
+
test: string;
|
|
52
|
+
status: 'pass' | 'fail' | 'skip' | 'warn' | 'suppressed';
|
|
53
|
+
severity: 'critical' | 'major' | 'minor' | 'info';
|
|
54
|
+
message?: string;
|
|
55
|
+
screenshot?: string;
|
|
56
|
+
duration: number;
|
|
57
|
+
}
|
|
58
|
+
export interface TestContext {
|
|
59
|
+
siteUrl: string;
|
|
60
|
+
browser: Browser;
|
|
61
|
+
browserContext: BrowserContext;
|
|
62
|
+
profile: SiteProfile;
|
|
63
|
+
adapter: HostingAdapter;
|
|
64
|
+
mode: ExecutionMode;
|
|
65
|
+
options: TestOptions;
|
|
66
|
+
}
|
|
67
|
+
export interface TestPlugin {
|
|
68
|
+
name: string;
|
|
69
|
+
description: string;
|
|
70
|
+
requiredModules?: string[];
|
|
71
|
+
dependsOn?: string[];
|
|
72
|
+
readOnly?: boolean;
|
|
73
|
+
needsGeneratedContent?: boolean;
|
|
74
|
+
canRun(profile: SiteProfile): boolean;
|
|
75
|
+
run(ctx: TestContext): Promise<TestResult[]>;
|
|
76
|
+
}
|
|
77
|
+
export interface CommandResult {
|
|
78
|
+
stdout: string;
|
|
79
|
+
stderr: string;
|
|
80
|
+
exitCode: number;
|
|
81
|
+
}
|
|
82
|
+
export interface HostingAdapter {
|
|
83
|
+
name: string;
|
|
84
|
+
canConnect(siteUrl: string): Promise<boolean>;
|
|
85
|
+
drush(command: string, args?: string[]): Promise<CommandResult>;
|
|
86
|
+
composer(command: string, args?: string[]): Promise<CommandResult>;
|
|
87
|
+
getLoginUrl(siteUrl: string): Promise<string>;
|
|
88
|
+
}
|
|
89
|
+
export type ExecutionMode = 'full' | 'read-only' | 'admin-only';
|
|
90
|
+
export type Severity = 'critical' | 'major' | 'minor' | 'info';
|
|
91
|
+
export interface TestOptions {
|
|
92
|
+
url: string;
|
|
93
|
+
mode: ExecutionMode;
|
|
94
|
+
plugins?: string[];
|
|
95
|
+
output: 'text' | 'json' | 'markdown';
|
|
96
|
+
report?: string;
|
|
97
|
+
exitCode: boolean;
|
|
98
|
+
failOn: Severity;
|
|
99
|
+
headed: boolean;
|
|
100
|
+
screenshots?: string;
|
|
101
|
+
/** Visual regression baseline directory */
|
|
102
|
+
vrBaseline?: string;
|
|
103
|
+
/** Visual regression diff threshold percentage */
|
|
104
|
+
vrThreshold?: number;
|
|
105
|
+
/** Content types to limit testing to */
|
|
106
|
+
contentTypes?: string[];
|
|
107
|
+
/** Plugins to skip (from .bolt.yml) */
|
|
108
|
+
configSkip?: string[];
|
|
109
|
+
/** Full .bolt.yml config (passed through for runner/plugin access) */
|
|
110
|
+
boltConfig?: import('./config.js').BoltConfig;
|
|
111
|
+
/** Skip .boltrc.yml suppression (used by `bolt suppress` to see raw failures) */
|
|
112
|
+
skipSuppression?: boolean;
|
|
113
|
+
}
|
|
114
|
+
export interface UpdateCandidate {
|
|
115
|
+
name: string;
|
|
116
|
+
currentVersion: string;
|
|
117
|
+
latestVersion: string;
|
|
118
|
+
/** semver-safe-update | update-possible */
|
|
119
|
+
updateType: string;
|
|
120
|
+
description?: string;
|
|
121
|
+
isSecurityUpdate: boolean;
|
|
122
|
+
}
|
|
123
|
+
export interface UpdateResult {
|
|
124
|
+
package: string;
|
|
125
|
+
oldVersion: string;
|
|
126
|
+
newVersion: string;
|
|
127
|
+
status: 'pass' | 'fail' | 'skip' | 'conflict';
|
|
128
|
+
branch: string;
|
|
129
|
+
commitHash?: string;
|
|
130
|
+
testSummary?: {
|
|
131
|
+
passed: number;
|
|
132
|
+
failed: number;
|
|
133
|
+
suppressed: number;
|
|
134
|
+
};
|
|
135
|
+
configChanges?: string[];
|
|
136
|
+
dbUpdates?: number;
|
|
137
|
+
error?: string;
|
|
138
|
+
duration: number;
|
|
139
|
+
}
|
|
140
|
+
export interface UpdateReport {
|
|
141
|
+
timestamp: string;
|
|
142
|
+
baseBranch: string;
|
|
143
|
+
totalCandidates: number;
|
|
144
|
+
updated: number;
|
|
145
|
+
failed: number;
|
|
146
|
+
skipped: number;
|
|
147
|
+
duration: number;
|
|
148
|
+
results: UpdateResult[];
|
|
149
|
+
}
|
|
150
|
+
export interface TestReport {
|
|
151
|
+
site: string;
|
|
152
|
+
timestamp: string;
|
|
153
|
+
duration: number;
|
|
154
|
+
mode: ExecutionMode;
|
|
155
|
+
summary: {
|
|
156
|
+
pluginsRan: number;
|
|
157
|
+
pluginsSkipped: number;
|
|
158
|
+
totalTests: number;
|
|
159
|
+
passed: number;
|
|
160
|
+
failed: number;
|
|
161
|
+
skipped: number;
|
|
162
|
+
warned: number;
|
|
163
|
+
suppressed: number;
|
|
164
|
+
worstSeverity: string | null;
|
|
165
|
+
};
|
|
166
|
+
results: TestResult[];
|
|
167
|
+
plugins: PluginSummary[];
|
|
168
|
+
}
|
|
169
|
+
export interface PluginSummary {
|
|
170
|
+
name: string;
|
|
171
|
+
status: 'ran' | 'skipped';
|
|
172
|
+
reason?: string;
|
|
173
|
+
tests: number;
|
|
174
|
+
passed: number;
|
|
175
|
+
failed: number;
|
|
176
|
+
skipped: number;
|
|
177
|
+
duration: number;
|
|
178
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
services:
|
|
2
|
+
bolt_inspect.site_profiler:
|
|
3
|
+
class: Drupal\bolt_inspect\Service\SiteProfiler
|
|
4
|
+
arguments:
|
|
5
|
+
- '@entity_type.manager'
|
|
6
|
+
- '@entity_field.manager'
|
|
7
|
+
- '@extension.list.module'
|
|
8
|
+
- '@router.route_provider'
|
|
9
|
+
- '@menu.link_tree'
|
|
10
|
+
|
|
11
|
+
bolt_inspect.content_generator:
|
|
12
|
+
class: Drupal\bolt_inspect\Service\ContentGenerator
|
|
13
|
+
arguments:
|
|
14
|
+
- '@entity_type.manager'
|
|
15
|
+
- '@entity_field.manager'
|
|
16
|
+
- '@bolt_inspect.entity_tracker'
|
|
17
|
+
|
|
18
|
+
bolt_inspect.entity_tracker:
|
|
19
|
+
class: Drupal\bolt_inspect\Service\TestEntityTracker
|
|
20
|
+
arguments:
|
|
21
|
+
- '@state'
|
|
22
|
+
- '@entity_type.manager'
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bolt/bolt_inspect",
|
|
3
|
+
"description": "Drupal site introspection and test content management for Bolt CLI",
|
|
4
|
+
"type": "drupal-module",
|
|
5
|
+
"license": "GPL-2.0-or-later",
|
|
6
|
+
"require": {
|
|
7
|
+
"drupal/core": "^11"
|
|
8
|
+
},
|
|
9
|
+
"extra": {
|
|
10
|
+
"drush": {
|
|
11
|
+
"services": {
|
|
12
|
+
"drush.services.yml": "^13"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
services:
|
|
2
|
+
bolt_inspect.commands:
|
|
3
|
+
class: Drupal\bolt_inspect\Drush\Commands\BoltInspectCommands
|
|
4
|
+
arguments:
|
|
5
|
+
- '@bolt_inspect.site_profiler'
|
|
6
|
+
- '@bolt_inspect.content_generator'
|
|
7
|
+
- '@bolt_inspect.entity_tracker'
|
|
8
|
+
- '@entity_type.manager'
|
|
9
|
+
tags:
|
|
10
|
+
- { name: drush.command }
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
declare(strict_types=1);
|
|
4
|
+
|
|
5
|
+
namespace Drupal\bolt_inspect\Drush\Commands;
|
|
6
|
+
|
|
7
|
+
use Drupal\bolt_inspect\Service\ContentGenerator;
|
|
8
|
+
use Drupal\bolt_inspect\Service\SiteProfiler;
|
|
9
|
+
use Drupal\bolt_inspect\Service\TestEntityTracker;
|
|
10
|
+
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
|
11
|
+
use Drush\Attributes as CLI;
|
|
12
|
+
use Drush\Commands\DrushCommands;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Drush commands for Bolt site inspection and test content management.
|
|
16
|
+
*/
|
|
17
|
+
class BoltInspectCommands extends DrushCommands {
|
|
18
|
+
|
|
19
|
+
public function __construct(
|
|
20
|
+
private readonly SiteProfiler $profiler,
|
|
21
|
+
private readonly ContentGenerator $generator,
|
|
22
|
+
private readonly TestEntityTracker $tracker,
|
|
23
|
+
private readonly EntityTypeManagerInterface $entityTypeManager,
|
|
24
|
+
) {
|
|
25
|
+
parent::__construct();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Return full site structure as JSON.
|
|
30
|
+
*/
|
|
31
|
+
#[CLI\Command(name: 'bolt-inspect:profile')]
|
|
32
|
+
#[CLI\Usage(name: 'drush bolt-inspect:profile', description: 'Output site profile as JSON')]
|
|
33
|
+
public function profile(): void {
|
|
34
|
+
$profile = $this->profiler->profile();
|
|
35
|
+
$this->output()->writeln(json_encode($profile, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Generate test content — one node per content type with required fields.
|
|
40
|
+
*/
|
|
41
|
+
#[CLI\Command(name: 'bolt-inspect:generate')]
|
|
42
|
+
#[CLI\Usage(name: 'drush bolt-inspect:generate', description: 'Create test content for all content types')]
|
|
43
|
+
public function generate(): void {
|
|
44
|
+
if ($this->tracker->hasTracked()) {
|
|
45
|
+
$this->logger()->notice('Test content already exists. Run bolt-inspect:cleanup first or use existing content.');
|
|
46
|
+
$this->listTracked();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
$this->logger()->notice('Generating test content...');
|
|
51
|
+
$results = $this->generator->generateAll();
|
|
52
|
+
|
|
53
|
+
$rows = [];
|
|
54
|
+
$errors = 0;
|
|
55
|
+
foreach ($results as $bundle => $result) {
|
|
56
|
+
if ($result['status'] === 'created') {
|
|
57
|
+
$rows[] = [$bundle, 'CREATED', $result['nid'], $result['label']];
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
$rows[] = [$bundle, 'ERROR', '', $result['error'] ?? 'Unknown error'];
|
|
61
|
+
$errors++;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
$this->io()->table(['Content Type', 'Status', 'NID', 'Detail'], $rows);
|
|
66
|
+
|
|
67
|
+
$total = count($results);
|
|
68
|
+
$created = $total - $errors;
|
|
69
|
+
$this->logger()->success("Generated {$created}/{$total} content types.");
|
|
70
|
+
|
|
71
|
+
if ($errors > 0) {
|
|
72
|
+
$this->logger()->warning("{$errors} content type(s) had errors.");
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Remove all generated test content.
|
|
78
|
+
*/
|
|
79
|
+
#[CLI\Command(name: 'bolt-inspect:cleanup')]
|
|
80
|
+
#[CLI\Usage(name: 'drush bolt-inspect:cleanup', description: 'Remove all bolt test content')]
|
|
81
|
+
public function cleanup(): void {
|
|
82
|
+
if (!$this->tracker->hasTracked()) {
|
|
83
|
+
$this->logger()->notice('No test content to clean up.');
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
$tracked = $this->tracker->getTracked();
|
|
88
|
+
$count = count($tracked);
|
|
89
|
+
$this->logger()->notice("Cleaning up {$count} tracked entities...");
|
|
90
|
+
|
|
91
|
+
$counts = $this->tracker->cleanupAll();
|
|
92
|
+
|
|
93
|
+
$rows = [];
|
|
94
|
+
foreach ($counts as $type => $deleted) {
|
|
95
|
+
$rows[] = [$type, $deleted];
|
|
96
|
+
}
|
|
97
|
+
$this->io()->table(['Entity Type', 'Deleted'], $rows);
|
|
98
|
+
|
|
99
|
+
$total = array_sum($counts);
|
|
100
|
+
$this->logger()->success("Cleaned up {$total} entities.");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* List all currently tracked test entities.
|
|
105
|
+
*/
|
|
106
|
+
#[CLI\Command(name: 'bolt-inspect:list')]
|
|
107
|
+
#[CLI\Usage(name: 'drush bolt-inspect:list', description: 'List tracked test entities')]
|
|
108
|
+
public function listTracked(): void {
|
|
109
|
+
$tracked = $this->tracker->getTracked();
|
|
110
|
+
|
|
111
|
+
if (empty($tracked)) {
|
|
112
|
+
$this->logger()->notice('No tracked test entities.');
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
$rows = [];
|
|
117
|
+
foreach ($tracked as $entry) {
|
|
118
|
+
$rows[] = [
|
|
119
|
+
$entry['entity_type'],
|
|
120
|
+
$entry['id'],
|
|
121
|
+
$entry['label'],
|
|
122
|
+
date('Y-m-d H:i:s', $entry['created']),
|
|
123
|
+
];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
$this->io()->table(['Entity Type', 'ID', 'Label', 'Created'], $rows);
|
|
127
|
+
$this->logger()->notice(count($tracked) . ' tracked entities.');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Render test nodes and report status per content type.
|
|
132
|
+
*
|
|
133
|
+
* Loads each unpublished test node, renders it in isolation, and returns
|
|
134
|
+
* a JSON array with status, nid, title, html_length, or error per bundle.
|
|
135
|
+
*/
|
|
136
|
+
#[CLI\Command(name: 'bolt-inspect:render-check')]
|
|
137
|
+
#[CLI\Usage(name: 'drush bolt-inspect:render-check', description: 'Render-check all generated test nodes')]
|
|
138
|
+
public function renderCheck(): void {
|
|
139
|
+
$nodeStorage = $this->entityTypeManager->getStorage('node');
|
|
140
|
+
$viewBuilder = $this->entityTypeManager->getViewBuilder('node');
|
|
141
|
+
$renderer = \Drupal::service('renderer');
|
|
142
|
+
|
|
143
|
+
$nodeTypes = $this->entityTypeManager->getStorage('node_type')->loadMultiple();
|
|
144
|
+
$results = [];
|
|
145
|
+
|
|
146
|
+
foreach ($nodeTypes as $nodeType) {
|
|
147
|
+
$bundle = $nodeType->id();
|
|
148
|
+
|
|
149
|
+
// Find the test node for this bundle.
|
|
150
|
+
$nodes = $nodeStorage->loadByProperties([
|
|
151
|
+
'type' => $bundle,
|
|
152
|
+
'status' => 0,
|
|
153
|
+
'uid' => 1,
|
|
154
|
+
]);
|
|
155
|
+
|
|
156
|
+
// Filter to bolt-generated nodes.
|
|
157
|
+
$node = NULL;
|
|
158
|
+
foreach ($nodes as $candidate) {
|
|
159
|
+
$title = $candidate->label() ?? '';
|
|
160
|
+
if (str_starts_with($title, 'Bolt Test:') || str_starts_with($title, 'Bolt test')) {
|
|
161
|
+
$node = $candidate;
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (!$node) {
|
|
167
|
+
$results[] = [
|
|
168
|
+
'bundle' => $bundle,
|
|
169
|
+
'label' => $nodeType->label(),
|
|
170
|
+
'status' => 'missing',
|
|
171
|
+
];
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
$build = $viewBuilder->view($node, 'full');
|
|
177
|
+
$html = $renderer->renderInIsolation($build);
|
|
178
|
+
$len = strlen((string) $html);
|
|
179
|
+
|
|
180
|
+
$results[] = [
|
|
181
|
+
'bundle' => $bundle,
|
|
182
|
+
'label' => $nodeType->label(),
|
|
183
|
+
'status' => 'ok',
|
|
184
|
+
'nid' => (int) $node->id(),
|
|
185
|
+
'title' => $node->label(),
|
|
186
|
+
'html_length' => $len,
|
|
187
|
+
];
|
|
188
|
+
}
|
|
189
|
+
catch (\Throwable $e) {
|
|
190
|
+
$results[] = [
|
|
191
|
+
'bundle' => $bundle,
|
|
192
|
+
'label' => $nodeType->label(),
|
|
193
|
+
'status' => 'error',
|
|
194
|
+
'nid' => (int) $node->id(),
|
|
195
|
+
'error' => $e->getMessage(),
|
|
196
|
+
];
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
$this->output()->writeln(json_encode($results, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
}
|