@pressgang-wp/shakedown 0.1.1 → 0.1.3
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 +1 -1
- package/bin/seed-states.php +81 -19
- package/lib/sandbox.mjs +9 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -45,7 +45,7 @@ Optional `shakedown.config.json` in the theme, for overrides only:
|
|
|
45
45
|
|
|
46
46
|
Authored journeys (form submissions, checkout flows) live in your theme's `tests/e2e/` — when present they run alongside the derived passes as the `journeys` project.
|
|
47
47
|
|
|
48
|
-
**Seeding is convention-first.** If your theme ships [Muster](https://github.com/pressgang-wp/pressgang-muster) seeders (`
|
|
48
|
+
**Seeding is convention-first.** If your theme ships [Muster](https://github.com/pressgang-wp/pressgang-muster) seeders (a top-level `muster/` directory), the sandbox runs them via `wp capstan seed` as the baseline — your real menus, terms and pages — then layers the derived populated/minimal ACF state fixtures on top. A theme that ships no seeders is unaffected: the derived layer covers it on its own, so it does a good job out of the box.
|
|
49
49
|
|
|
50
50
|
Sandbox fixture randomness and time are separate deterministic inputs. `seed`
|
|
51
51
|
controls generated values; `epoch` fixes relative dates used by Muster,
|
package/bin/seed-states.php
CHANGED
|
@@ -4,11 +4,16 @@
|
|
|
4
4
|
* the sandbox isolation witness has already proven this WordPress runs on
|
|
5
5
|
* throwaway SQLite before this script is invoked).
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* Each ACF field group is seeded according to where it is located:
|
|
8
|
+
* - post types, page and post templates → a dedicated PAIR of fixtures,
|
|
9
|
+
* `populated` (every generatable field filled) and `minimal` (required
|
|
10
|
+
* fields only — the sparsest publishable state, where empty-link and
|
|
11
|
+
* missing-image bugs live), each with a placeholder featured image;
|
|
12
|
+
* - options pages → the populated values written once (global chrome);
|
|
13
|
+
* - the front page (`page_type=front_page`) → the populated values onto the
|
|
14
|
+
* home page, reusing a theme-assigned front page or creating one;
|
|
15
|
+
* - nav-menu-item groups → left to the theme's own seeder, whose menus carry
|
|
16
|
+
* the real structure the fields attach to.
|
|
12
17
|
*
|
|
13
18
|
* Run via: wp eval-file bin/seed-states.php <muster-autoload> <acf-json-dir> <seed> <epoch>
|
|
14
19
|
* Emits JSON: {"routes": [{url, kind, expect}...]} for the matrix.
|
|
@@ -51,13 +56,23 @@ $generator = new AcfValueGenerator($context->victuals(), [
|
|
|
51
56
|
->id(),
|
|
52
57
|
'post' => function (array $types) use ($context): int {
|
|
53
58
|
$type = $types[0] ?? 'post';
|
|
59
|
+
$slug = 'state-related-' . sanitize_title($type);
|
|
54
60
|
|
|
55
|
-
|
|
61
|
+
$ref = (new PostBuilder($context, $type))
|
|
56
62
|
->title('State related ' . $type)
|
|
57
|
-
->slug(
|
|
63
|
+
->slug($slug)
|
|
58
64
|
->status('publish')
|
|
59
|
-
|
|
60
|
-
|
|
65
|
+
// Pin before the fixture epoch so relationship stubs sink below the
|
|
66
|
+
// real fixtures in date-ordered feeds rather than heading them.
|
|
67
|
+
->date($context->clock()->epoch()->modify('-1 year')->format('Y-m-d H:i:s'))
|
|
68
|
+
->save();
|
|
69
|
+
|
|
70
|
+
(new AttachmentBuilder($context, "{$slug}-thumb"))
|
|
71
|
+
->placeholder(1200, 800)
|
|
72
|
+
->featuredOn($ref)
|
|
73
|
+
->save();
|
|
74
|
+
|
|
75
|
+
return $ref->id();
|
|
61
76
|
},
|
|
62
77
|
'term' => fn (string $taxonomy): int => (new TermBuilder($context, $taxonomy))
|
|
63
78
|
->name('State term')
|
|
@@ -68,43 +83,90 @@ $generator = new AcfValueGenerator($context->victuals(), [
|
|
|
68
83
|
]);
|
|
69
84
|
|
|
70
85
|
$routes = [];
|
|
86
|
+
$liveAcf = new LiveAcfAdapter();
|
|
71
87
|
|
|
72
88
|
foreach (AcfJson::groups($acfJsonDir) as $group) {
|
|
73
89
|
$targets = AcfJson::targets($group);
|
|
74
90
|
|
|
75
91
|
if ($targets === []) {
|
|
76
|
-
continue; //
|
|
92
|
+
continue; // no seedable location (taxonomy terms, user roles, …)
|
|
77
93
|
}
|
|
78
94
|
|
|
79
95
|
$target = $targets[0];
|
|
96
|
+
$fields = (array) $group['fields'];
|
|
97
|
+
$slugBase = sanitize_title($group['title'] ?? $group['key']);
|
|
80
98
|
|
|
81
99
|
// Options-page groups are global state, not a URL: seed the populated
|
|
82
|
-
// values once so chrome (header/footer) renders fully. The unseeded
|
|
83
|
-
//
|
|
100
|
+
// values once so chrome (header/footer) renders fully. The unseeded fresh
|
|
101
|
+
// install already exercises the empty-options state.
|
|
84
102
|
if ($target['param'] === 'options_page') {
|
|
85
|
-
|
|
103
|
+
$liveAcf->updateFields($generator->populated($fields), 'option', 0);
|
|
86
104
|
continue;
|
|
87
105
|
}
|
|
88
|
-
$slugBase = sanitize_title($group['title'] ?? $group['key']);
|
|
89
106
|
|
|
107
|
+
// The front page is a singular surface — seed the populated state onto the
|
|
108
|
+
// one home page. Reuse the front page a theme seeder may already have set;
|
|
109
|
+
// assign one only when none exists, so we never fight a theme-authored home.
|
|
110
|
+
// The matrix's own `/` route covers it, so no dedicated route is emitted.
|
|
111
|
+
// Other page_type values (top_level, …) have no generic surface to seed.
|
|
112
|
+
if ($target['param'] === 'page_type') {
|
|
113
|
+
if ($target['value'] !== 'front_page') {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
$frontId = (int) get_option('page_on_front');
|
|
118
|
+
if ($frontId === 0) {
|
|
119
|
+
$frontId = (new PostBuilder($context, 'page'))
|
|
120
|
+
->title('Home')
|
|
121
|
+
->slug('state-front-page')
|
|
122
|
+
->status('publish')
|
|
123
|
+
->date($fixtureDate)
|
|
124
|
+
->save()
|
|
125
|
+
->id();
|
|
126
|
+
update_option('show_on_front', 'page');
|
|
127
|
+
update_option('page_on_front', $frontId);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
$liveAcf->updateFields($generator->populated($fields), 'post', $frontId);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// nav_menu_item groups attach to a menu's items, which exist only once a
|
|
135
|
+
// menu is built with the theme's real structure — that is the theme
|
|
136
|
+
// seeder's job. The derived path leaves them to the baseline rather than
|
|
137
|
+
// inventing a menu here.
|
|
138
|
+
if ($target['param'] === 'nav_menu_item') {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Per-instance surfaces (post types, page and post templates): a dedicated
|
|
143
|
+
// populated AND minimal fixture, so the sparsest publishable state is
|
|
144
|
+
// exercised alongside the full one.
|
|
90
145
|
foreach (['populated', 'minimal'] as $variant) {
|
|
91
|
-
$fields = (array) $group['fields'];
|
|
92
146
|
$values = $variant === 'populated' ? $generator->populated($fields) : $generator->minimal($fields);
|
|
93
147
|
|
|
94
|
-
$builder = $target['param']
|
|
95
|
-
|
|
96
|
-
|
|
148
|
+
$builder = match ($target['param']) {
|
|
149
|
+
'page_template' => (new PostBuilder($context, 'page'))->template($target['value']),
|
|
150
|
+
'post_template' => (new PostBuilder($context, 'post'))->template($target['value']),
|
|
151
|
+
default => new PostBuilder($context, $target['value']),
|
|
152
|
+
};
|
|
97
153
|
|
|
98
154
|
$ref = $builder
|
|
99
155
|
->title(($group['title'] ?? $group['key']) . ' — ' . $variant)
|
|
100
156
|
->slug("state-{$slugBase}-{$variant}")
|
|
101
157
|
->status('publish')
|
|
102
158
|
->date($fixtureDate)
|
|
103
|
-
|
|
104
159
|
->content('State fixture: ' . $slugBase . ' (' . $variant . ')')
|
|
105
160
|
->acf($values)
|
|
106
161
|
->save();
|
|
107
162
|
|
|
163
|
+
// A placeholder featured image so archive and card thumbnails render
|
|
164
|
+
// instead of the theme's empty-thumbnail fallback.
|
|
165
|
+
(new AttachmentBuilder($context, "state-thumb-{$slugBase}-{$variant}"))
|
|
166
|
+
->placeholder(1200, 800)
|
|
167
|
+
->featuredOn($ref)
|
|
168
|
+
->save();
|
|
169
|
+
|
|
108
170
|
$url = get_permalink($ref->id());
|
|
109
171
|
|
|
110
172
|
if ($url) {
|
package/lib/sandbox.mjs
CHANGED
|
@@ -285,6 +285,12 @@ export async function bootSandbox(target, options = {}) {
|
|
|
285
285
|
}
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
+
// Clear WordPress's install defaults (Hello world!, Sample Page, the seeded
|
|
289
|
+
// comment) so the sandbox starts from a deterministic empty state — only
|
|
290
|
+
// seeded fixtures exist, and no version-dependent defaults leak into feeds,
|
|
291
|
+
// archives, or menus.
|
|
292
|
+
wp(root, ['site', 'empty', '--yes']);
|
|
293
|
+
|
|
288
294
|
wp(root, ['rewrite', 'structure', '/%postname%/', '--hard']);
|
|
289
295
|
|
|
290
296
|
const server = spawn('wp', ['server', `--host=127.0.0.1`, `--port=${port}`, `--docroot=${docroot}`, `--path=${root}`], {
|
|
@@ -308,7 +314,7 @@ export async function bootSandbox(target, options = {}) {
|
|
|
308
314
|
|
|
309
315
|
/**
|
|
310
316
|
* Convention-first baseline seeding: run the theme's OWN Muster seeders
|
|
311
|
-
* (anything under its `
|
|
317
|
+
* (anything under its `muster/` directory) via `wp capstan seed`, so a theme that
|
|
312
318
|
* ships fixtures gets its real content — menus assigned to locations, terms,
|
|
313
319
|
* pages, relationships — exactly as `wp capstan seed` produces on a dev site.
|
|
314
320
|
* The ACF state fixtures (see {@link seedAcfStates}) then layer on top.
|
|
@@ -323,7 +329,8 @@ export async function bootSandbox(target, options = {}) {
|
|
|
323
329
|
* @returns {boolean} True when theme seeders ran; false when none were applied.
|
|
324
330
|
*/
|
|
325
331
|
export function seedThemeMuster(sandbox, { seed = 42 } = {}) {
|
|
326
|
-
|
|
332
|
+
// Seeders live in a top-level `muster/` directory (mapped under autoload-dev).
|
|
333
|
+
const musterDir = join(sandbox.root, 'wp-content', 'themes', sandbox.theme ?? '', 'muster');
|
|
327
334
|
|
|
328
335
|
if (!sandbox.theme || !existsSync(musterDir)) {
|
|
329
336
|
return false;
|