Skip to content

Examples suite: one backend, four apps, and a CLI, with tests to match - #785

Open
Decipher wants to merge 17 commits into
developfrom
feature/examples-consolidation
Open

Examples suite: one backend, four apps, and a CLI, with tests to match#785
Decipher wants to merge 17 commits into
developfrom
feature/examples-consolidation

Conversation

@Decipher

@Decipher Decipher commented Aug 18, 2026

Copy link
Copy Markdown
Member

There are plenty of Druxt examples scattered around, but until now there
hasn't been one place where you can boot a real backend and click
through every core module working against it, with tests to prove it.
This PR consolidates the standalone examples into a suite built around
that idea.

What's in the suite

Example What it demonstrates
druxt-site The vanilla reference: the Umami site on DruxtSite with a minimal wireframe theme, /examples/* pattern pages, and working language switching
druxt-daisyui (Recipe Box) A swipeable recipe deck. DruxtSite styled with DaisyUI, driven by the wildcard router
druxt-tailwind (Meal Planner) A weekly meal planner built with plain Vite + Vue, using druxt/getCollection directly
druxt-bootstrapvue (Content Ops Console) An editorial console with optimistic inline editing and full-record editing through DruxtEntityForm, over a real OAuth2 write path
node-client (druxt-inspect) A CLI built on DruxtClient/DruxtSchema alone, running in plain Node. Inspect types, schemas, views, and generate wrapper stubs

Each concept app answers a fair question: can Druxt power something that
isn't a brochure site? They share the same Umami backend and each uses a
different slice of the module set.

The backend

docs/drupal is now a self-contained backend for the whole suite:
SQLite plus Tome provisioning (no Docker), committed Umami config and
content including Spanish translations, and an OAuth consumer for the
authenticated examples.

Language switching works end to end, which took two local patches
(docs/drupal/patches/README.md explains each): one for
decoupled_router #3111456
so the language resolves from the requested path, and a rework of
druxt #3273228
(MR9) so view routes carry a langcode. Both are intended to become the
replacement patches on their issues. A Cypress multilingual battery pins
the behaviour, including the one known gap: field labels don't translate
yet because schemas have no language dimension.

Framework fixes

Building the suite surfaced three small bugs in the packages themselves,
fixed here with tests: DruxtModule wrapper resolution, how
DruxtEntityForm reports network failures, and how the router store
handles routes.

CI

Each app gets lint and test jobs, plus a manual examples:preview job
that boots the backend and all four frontends for live review. The
GitHub workflow is untouched. The added pipeline config is additive.

Trying it

cd docs/drupal && make provision && make start
cd examples/druxt-site && yarn install && yarn dev

Sign in with admin / druxt123 (demo credentials) to try the write
paths in the Content Ops Console.

docs/drupal becomes the one backend every example runs against: SQLite +
Tome provisioning via .devtools (assemble/provision/start), committed
Umami config and content with Spanish translations, form displays
trimmed to the fields editors need, OAuth consumer wiring for the
authenticated examples, and local patches (decoupled_router #3111456,
druxt #3273228) that make language switching resolve from the requested
path. See docs/drupal/patches/README.md for why each is held locally.
Small fixes to druxt, druxt-entity and druxt-router found while
exercising them across the suite: DruxtModule wrapper resolution,
DruxtEntityForm network-failure handling (with test), and router store
route handling. Each was reproduced in an example before being fixed.
The examples get their own CI surface: per-app lint/test jobs, a
structure check, cspell/knip/markdownlint config covering the new trees,
and a manual examples:preview job that boots the backend and all four
apps behind Cloudflare tunnels with a code-server (with recommended
extensions) for live review. Root docs updated to describe the suite.
The _deprecated/* standalone examples (debug, entity-explorer,
entity-queries, entity-form, menu-edit, router-modes, schema, wrappers,
custom-module) fold into examples/druxt-site as /examples/* pattern
pages on one vanilla DruxtSite app: the Umami site with a minimal
wireframe theme, working language switching pinned by a Cypress
multilingual battery, and shared Cypress helpers under examples/shared.
scripts/check-examples.js enforces the suite's structure; the docs
guides point at the consolidated example.
Three apps that prove Druxt powers things that aren't brochure sites,
each on the same Umami backend: Recipe Box (druxt-daisyui), a
swipeable recipe deck; Meal Planner (druxt-tailwind), a no-Nuxt Vite
app planning a week of meals via druxt/getCollection alone; and Content
Ops Console (druxt-bootstrapvue), an editorial console with optimistic
inline editing and full-record editing through DruxtEntityForm over a
real OAuth2 write path. Each carries its own Cypress battery.
A framework-agnostic CLI (no Vue, no Nuxt) for inspecting a Drupal
JSON:API backend through the same classes every other example uses under
the hood: resource types, view/form schemas, entity samples, views, and
generated Druxt Entity wrapper stubs. Jest tests run against recorded
fixtures, so the suite needs no live backend.
@changeset-bot

changeset-bot Bot commented Aug 18, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 469ee6c

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 323 files, which is 223 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

Usage-priced reviews support at most 300 files.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 37b7c7a6-2fb5-453c-b865-ee7062b6317f

📥 Commits

Reviewing files that changed from the base of the PR and between 5c98a39 and 469ee6c.

⛔ Files ignored due to path filters (12)
  • examples/_deprecated/entity-explorer/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • examples/_deprecated/entity-queries/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • examples/_deprecated/menu-edit/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • examples/_deprecated/router-modes/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • examples/_deprecated/schema-component/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • examples/_deprecated/wrappers/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • examples/custom-module/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • examples/druxt-bootstrapvue/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • examples/druxt-daisyui/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • examples/druxt-site/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • examples/druxt-tailwind/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • examples/node-client/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (323)
  • .cspell-project-words.txt
  • .cspell.json
  • .eslintrc.js
  • .github/dependabot.yml
  • .github/workflows/ci.yml
  • .gitignore
  • .gitlab-ci.yml
  • .gitpod.yml
  • .gitpod/Dockerfile
  • .gitpod/WELCOME.md
  • .gitpod/scripts/ddev-download.sh
  • .gitpod/scripts/ddev-setup.sh
  • .gitpod/scripts/drupal-setup.sh
  • .gitpod/scripts/env-setup.sh
  • .markdownlint-cli2.jsonc
  • AGENTS.md
  • CONTRIBUTING.md
  • README.md
  • docs/drupal/.ddev/commands/web/drupal-install
  • docs/drupal/.ddev/commands/web/gitpod-config
  • docs/drupal/.ddev/config.yaml
  • docs/drupal/.ddev/docker-compose.env.yaml
  • docs/drupal/.ddev/docker-compose.network-mtu.yaml
  • docs/drupal/.devtools/README.md
  • docs/drupal/.devtools/assemble
  • docs/drupal/.devtools/consumer-cleanup.php
  • docs/drupal/.devtools/consumer-set-redirect.php
  • docs/drupal/.devtools/etc/php.ini
  • docs/drupal/.devtools/helpers.php
  • docs/drupal/.devtools/image-derivatives.php
  • docs/drupal/.devtools/info
  • docs/drupal/.devtools/provision
  • docs/drupal/.devtools/serial-config-import.php
  • docs/drupal/.devtools/serial-content-delete.php
  • docs/drupal/.devtools/serial-content-import.php
  • docs/drupal/.devtools/set-admin-password.php
  • docs/drupal/.devtools/start
  • docs/drupal/.devtools/stop
  • docs/drupal/.gitignore
  • docs/drupal/Makefile
  • docs/drupal/README.md
  • docs/drupal/composer.json
  • docs/drupal/config/sync/core.entity_form_display.node.recipe.default.yml
  • docs/drupal/config/sync/core.entity_form_display.taxonomy_term.recipe_category.default.yml
  • docs/drupal/config/sync/core.entity_form_display.taxonomy_term.tags.default.yml
  • docs/drupal/config/sync/core.extension.yml
  • docs/drupal/config/sync/image.style.druxt_card_4_3.yml
  • docs/drupal/config/sync/image.style.druxt_thumb_4_3.yml
  • docs/drupal/config/sync/jsonapi.settings.yml
  • docs/drupal/config/sync/language/es/responsive_image.styles.hero.yml
  • docs/drupal/config/sync/language/es/views.view.related_recipes.yml
  • docs/drupal/config/sync/language/es/views.view.watchdog.yml
  • docs/drupal/config/sync/language/es/views.view.who_s_new.yml
  • docs/drupal/config/sync/simple_oauth.settings.yml
  • docs/drupal/content/consumer.e7043528-3e7c-44b1-9a78-6b05bc888698.json
  • docs/drupal/content/meta/index.json
  • docs/drupal/content/node.62a560b5-1954-4bf7-ba87-609ce73675e0.json
  • docs/drupal/content/user.8e90cdd3-6657-4f12-b1da-7fef01da4f83.json
  • docs/drupal/patches/README.md
  • docs/drupal/patches/decoupled_router-3111456-resolve-language-from-path.patch
  • docs/drupal/patches/druxt-3273228-views-route-langcode.patch
  • docs/drupal/web/sites/default/settings.php
  • docs/nuxt/content/guides/custom-module.md
  • docs/nuxt/content/guides/node-client.md
  • docs/nuxt/content/modules/site/getting-started.md
  • examples/README.md
  • examples/_deprecated/README.md
  • examples/_deprecated/debug-component/README.md
  • examples/_deprecated/debug-component/nuxt.config.js
  • examples/_deprecated/debug-component/package.json
  • examples/_deprecated/entity-explorer/README.md
  • examples/_deprecated/entity-explorer/components/druxt/entity/Default.vue
  • examples/_deprecated/entity-explorer/layouts/default.vue
  • examples/_deprecated/entity-explorer/nuxt.config.js
  • examples/_deprecated/entity-explorer/package.json
  • examples/_deprecated/entity-explorer/pages/index.vue
  • examples/_deprecated/entity-explorer/store/index.js
  • examples/_deprecated/entity-queries/README.md
  • examples/_deprecated/entity-queries/components/druxt/entity/node/Article.vue
  • examples/_deprecated/entity-queries/components/druxt/entity/node/RecipeTeaser.vue
  • examples/_deprecated/entity-queries/nuxt.config.js
  • examples/_deprecated/entity-queries/package.json
  • examples/_deprecated/menu-edit/components/Logo.vue
  • examples/_deprecated/menu-edit/components/druxt/menu/Main.vue
  • examples/_deprecated/menu-edit/nuxt.config.js
  • examples/_deprecated/menu-edit/package.json
  • examples/_deprecated/menu-edit/pages/index.vue
  • examples/_deprecated/router-modes/README.md
  • examples/_deprecated/router-modes/nuxt.config.js
  • examples/_deprecated/router-modes/package.json
  • examples/_deprecated/router-modes/pages/en/recipes/_slug.vue
  • examples/_deprecated/schema-component/README.md
  • examples/_deprecated/schema-component/nuxt.config.js
  • examples/_deprecated/schema-component/package.json
  • examples/_deprecated/wrappers/README.md
  • examples/_deprecated/wrappers/nuxt.config.js
  • examples/_deprecated/wrappers/package.json
  • examples/custom-module/README.md
  • examples/custom-module/components/druxt/CustomModule.vue
  • examples/custom-module/components/druxt/custom-module/Default.vue
  • examples/custom-module/nuxt.config.js
  • examples/custom-module/package.json
  • examples/custom-module/pages/index.vue
  • examples/druxt-bootstrapvue/README.md
  • examples/druxt-bootstrapvue/assets/css/main.css
  • examples/druxt-bootstrapvue/components/druxt/field/boolean_checkbox/Form.vue
  • examples/druxt-bootstrapvue/components/druxt/field/number/Form.vue
  • examples/druxt-bootstrapvue/components/druxt/field/options_select/Form.vue
  • examples/druxt-bootstrapvue/components/druxt/field/string_textfield/field_ingredients/Form.vue
  • examples/druxt-bootstrapvue/layouts/console.vue
  • examples/druxt-bootstrapvue/nuxt.config.js
  • examples/druxt-bootstrapvue/package.json
  • examples/druxt-bootstrapvue/pages/index.vue
  • examples/druxt-bootstrapvue/test/cypress.config.js
  • examples/druxt-bootstrapvue/test/cypress/e2e/nuxt/frontpage.cy.js
  • examples/druxt-bootstrapvue/test/cypress/support/e2e.js
  • examples/druxt-daisyui/README.md
  • examples/druxt-daisyui/assets/css/main.css
  • examples/druxt-daisyui/assets/js/recipes.js
  • examples/druxt-daisyui/components/RecipeDetailPanel.vue
  • examples/druxt-daisyui/components/druxt/entity/node/RecipeFull.vue
  • examples/druxt-daisyui/layouts/default.vue
  • examples/druxt-daisyui/nuxt.config.js
  • examples/druxt-daisyui/package.json
  • examples/druxt-daisyui/pages/index.vue
  • examples/druxt-daisyui/plugins/recipeBox.client.js
  • examples/druxt-daisyui/store/recipeBox.js
  • examples/druxt-daisyui/tailwind.config.js
  • examples/druxt-daisyui/test/cypress.config.js
  • examples/druxt-daisyui/test/cypress/e2e/nuxt/frontpage.cy.js
  • examples/druxt-daisyui/test/cypress/e2e/nuxt/recipe-box.cy.js
  • examples/druxt-daisyui/test/cypress/support/e2e.js
  • examples/druxt-site/DESIGN.md
  • examples/druxt-site/README.md
  • examples/druxt-site/assets/css/main.css
  • examples/druxt-site/components/DruxtCardGrid.vue
  • examples/druxt-site/components/SchemaComponent.vue
  • examples/druxt-site/components/druxt/block/HelpBlockUmami.vue
  • examples/druxt-site/components/druxt/block/LocalTasksBlockUmami.vue
  • examples/druxt-site/components/druxt/block/SearchFormBlockUmami.vue
  • examples/druxt-site/components/druxt/block/SystemBrandingBlockUmami.vue
  • examples/druxt-site/components/druxt/block/SystemMessagesBlockUmami.vue
  • examples/druxt-site/components/druxt/breadcrumb/Default.vue
  • examples/druxt-site/components/druxt/entity/node/Page.vue
  • examples/druxt-site/components/druxt/entity/node/Recipe.vue
  • examples/druxt-site/components/druxt/entity/node/article/CardCommonView.vue
  • examples/druxt-site/components/druxt/entity/node/recipe/CardCommonAltView.vue
  • examples/druxt-site/components/druxt/entity/node/recipe/CardCommonView.vue
  • examples/druxt-site/components/druxt/entity/taxonomy_term/Default.vue
  • examples/druxt-site/components/druxt/entity/user/Default.vue
  • examples/druxt-site/components/druxt/site/Umami.vue
  • examples/druxt-site/components/druxt/view/recipe_collections/Block.vue
  • examples/druxt-site/nuxt.config.js
  • examples/druxt-site/package.json
  • examples/druxt-site/pages/examples/custom-module.vue
  • examples/druxt-site/pages/examples/debug.vue
  • examples/druxt-site/pages/examples/entity-explorer.vue
  • examples/druxt-site/pages/examples/entity-form.vue
  • examples/druxt-site/pages/examples/entity-queries.vue
  • examples/druxt-site/pages/examples/index.vue
  • examples/druxt-site/pages/examples/menu-edit.vue
  • examples/druxt-site/pages/examples/reactive-filtering.vue
  • examples/druxt-site/pages/examples/router-modes.vue
  • examples/druxt-site/pages/examples/schema.vue
  • examples/druxt-site/pages/examples/views.vue
  • examples/druxt-site/pages/examples/wrappers.vue
  • examples/druxt-site/plugins/druxt-wrappers.js
  • examples/druxt-site/test/cypress.config.js
  • examples/druxt-site/test/cypress/e2e/nuxt/druxt-devel-template.cy.js
  • examples/druxt-site/test/cypress/e2e/nuxt/examples/custom-module.cy.js
  • examples/druxt-site/test/cypress/e2e/nuxt/examples/debug.cy.js
  • examples/druxt-site/test/cypress/e2e/nuxt/examples/entity-explorer.cy.js
  • examples/druxt-site/test/cypress/e2e/nuxt/examples/entity-form.cy.js
  • examples/druxt-site/test/cypress/e2e/nuxt/examples/entity-queries.cy.js
  • examples/druxt-site/test/cypress/e2e/nuxt/examples/index.cy.js
  • examples/druxt-site/test/cypress/e2e/nuxt/examples/menu-edit.cy.js
  • examples/druxt-site/test/cypress/e2e/nuxt/examples/reactive-filtering.cy.js
  • examples/druxt-site/test/cypress/e2e/nuxt/examples/router-modes.cy.js
  • examples/druxt-site/test/cypress/e2e/nuxt/examples/schema.cy.js
  • examples/druxt-site/test/cypress/e2e/nuxt/examples/wrappers.cy.js
  • examples/druxt-site/test/cypress/e2e/nuxt/multilingual.cy.js
  • examples/druxt-site/test/cypress/e2e/storybook/storybook.cy.js
  • examples/druxt-site/test/cypress/support/commands.js
  • examples/druxt-site/test/cypress/support/e2e.js
  • examples/druxt-tailwind/README.md
  • examples/druxt-tailwind/index.html
  • examples/druxt-tailwind/package.json
  • examples/druxt-tailwind/postcss.config.js
  • examples/druxt-tailwind/src/App.vue
  • examples/druxt-tailwind/src/assets/css/main.css
  • examples/druxt-tailwind/src/fetchMixin.js
  • examples/druxt-tailwind/src/main.js
  • examples/druxt-tailwind/src/pages/NotFound.vue
  • examples/druxt-tailwind/src/pages/Planner.vue
  • examples/druxt-tailwind/src/store/plan.js
  • examples/druxt-tailwind/tailwind.config.js
  • examples/druxt-tailwind/test/cypress.config.js
  • examples/druxt-tailwind/test/cypress/e2e/nuxt/frontpage.cy.js
  • examples/druxt-tailwind/test/cypress/support/e2e.js
  • examples/druxt-tailwind/vite.config.js
  • examples/entity-form/README.md
  • examples/entity-form/nuxt.config.js
  • examples/entity-form/package.json
  • examples/node-client/README.md
  • examples/node-client/bin/druxt-inspect.js
  • examples/node-client/index.js
  • examples/node-client/jest.config.js
  • examples/node-client/package.json
  • examples/node-client/src/args.js
  • examples/node-client/src/commands/sample.js
  • examples/node-client/src/commands/schema.js
  • examples/node-client/src/commands/stubs.js
  • examples/node-client/src/commands/types.js
  • examples/node-client/src/commands/views.js
  • examples/node-client/src/index.js
  • examples/node-client/test/adapter.js
  • examples/node-client/test/args.test.js
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_form_display_entity_form_display_filter_5Bdrupal_internal__id_-0737edf33aae96d6.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_form_display_entity_form_display_filter_5Bdrupal_internal__id_-0bf0093961a4b1d6.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_form_display_entity_form_display_filter_5Bdrupal_internal__id_-12674ea12722b39b.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_form_display_entity_form_display_filter_5Bdrupal_internal__id_-246be95bd179aedf.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_form_display_entity_form_display_filter_5Bdrupal_internal__id_-2d1910320363a766.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_form_display_entity_form_display_filter_5Bdrupal_internal__id_-384528aebd4400fe.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_form_display_entity_form_display_filter_5Bdrupal_internal__id_-4e356ca8a559f5f3.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_form_display_entity_form_display_filter_5Bdrupal_internal__id_-565a5903b55632e1.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_form_display_entity_form_display_filter_5Bdrupal_internal__id_-62ed99b5baf4a698.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_form_display_entity_form_display_filter_5Bdrupal_internal__id_-68ed582b9b9ff29b.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_form_display_entity_form_display_filter_5Bdrupal_internal__id_-790beeb665ae6037.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_form_display_entity_form_display_filter_5Bdrupal_internal__id_-830798ce178e1c95.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_form_display_entity_form_display_filter_5Bdrupal_internal__id_-9f51eb21cb25b128.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_form_display_entity_form_display_filter_5Bdrupal_internal__id_-a067bd20c815f4b2.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_form_display_entity_form_display_filter_5Bdrupal_internal__id_-a8e038e78c34ff64.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_form_display_entity_form_display_filter_5Bdrupal_internal__id_-b598d4f551458439.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_form_display_entity_form_display_filter_5Bdrupal_internal__id_-d6bec93b30418d48.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_form_display_entity_form_display_filter_5Bdrupal_internal__id_-e0dbbac898219a99.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_form_display_entity_form_display_filter_5Bdrupal_internal__id_-e486cc4c740b7908.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_form_display_entity_form_display_sort_drupal_internal__id-af7f0d3df279b142.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-09bb3bae4c5be31e.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-0f75d22bf4fa5f73.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-17bb43bda05a30b7.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-212b30f54a9f736c.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-2949cc5068ff9ee5.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-31f6aa7c1bc3b359.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-36de7dd14b6cec1c.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-393a379e05b90c26.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-4346f07a837f3b56.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-4fbfc3c11ba20126.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-5b23bd671f751c5d.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-5c99a6ad0e4ffda3.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-6a600490b90537d9.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-7002e42fe542f929.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-70939336b784ce92.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-78aec895009c19d7.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-8120041a91ed5eda.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-8348cd26904edd21.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-87af25f140b1f580.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-8838f1fe787a981c.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-888c1af9253482cc.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-8b8f209f27d07e88.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-94b0428c3a5c24aa.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-9565fae3182baba9.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-adc18567fb55116d.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-b08c07a446ea9a23.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-b12dbe8e832f6061.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-b190f0bc5c6fa7c2.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-b5aa4659e0fd7208.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-b7ee9a8a1e70440e.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-ba6543b4fda52878.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-bfbe857ccfedc90c.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-bfe68e376de67a35.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-cd8c3eddf358179c.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-d4c088ab78c3a02f.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-ecc29f7d5e9bc170.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-ed73eb9028fe8778.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_filter_5Bdrupal_internal__id_-fdfed65b9df2a93c.json
  • examples/node-client/test/fixtures/get-en_jsonapi_entity_view_display_entity_view_display_sort_drupal_internal__id-c58ba0e4b0860a52.json
  • examples/node-client/test/fixtures/get-en_jsonapi_field_config_field_config_filter_5Bentity_type_5D_block_content_filte-7064ade0b354acb2.json
  • examples/node-client/test/fixtures/get-en_jsonapi_field_config_field_config_filter_5Bentity_type_5D_block_content_filte-7a971f40a268001b.json
  • examples/node-client/test/fixtures/get-en_jsonapi_field_config_field_config_filter_5Bentity_type_5D_block_content_filte-b125b42f90bc1a92.json
  • examples/node-client/test/fixtures/get-en_jsonapi_field_config_field_config_filter_5Bentity_type_5D_block_content_filte-bcc10ec450b67a6e.json
  • examples/node-client/test/fixtures/get-en_jsonapi_field_config_field_config_filter_5Bentity_type_5D_contact_message_fil-b8b3acb93b57f01f.json
  • examples/node-client/test/fixtures/get-en_jsonapi_field_config_field_config_filter_5Bentity_type_5D_media_filter_5Bbund-3b754946916f3d18.json
  • examples/node-client/test/fixtures/get-en_jsonapi_field_config_field_config_filter_5Bentity_type_5D_media_filter_5Bbund-400d0b8b988dfcb8.json
  • examples/node-client/test/fixtures/get-en_jsonapi_field_config_field_config_filter_5Bentity_type_5D_media_filter_5Bbund-5eb2a5066b6a3fa6.json
  • examples/node-client/test/fixtures/get-en_jsonapi_field_config_field_config_filter_5Bentity_type_5D_media_filter_5Bbund-6456fc5048a83c3c.json
  • examples/node-client/test/fixtures/get-en_jsonapi_field_config_field_config_filter_5Bentity_type_5D_media_filter_5Bbund-c540bcebdde1fdbe.json
  • examples/node-client/test/fixtures/get-en_jsonapi_field_config_field_config_filter_5Bentity_type_5D_node_filter_5Bbundl-60c89272082e305d.json
  • examples/node-client/test/fixtures/get-en_jsonapi_field_config_field_config_filter_5Bentity_type_5D_node_filter_5Bbundl-d1516b0728bbd677.json
  • examples/node-client/test/fixtures/get-en_jsonapi_field_config_field_config_filter_5Bentity_type_5D_node_filter_5Bbundl-e063bc63fec66450.json
  • examples/node-client/test/fixtures/get-en_jsonapi_field_config_field_config_filter_5Bentity_type_5D_taxonomy_term_filte-567a635ad2790bb8.json
  • examples/node-client/test/fixtures/get-en_jsonapi_field_config_field_config_filter_5Bentity_type_5D_taxonomy_term_filte-6df2cd27abbb16c1.json
  • examples/node-client/test/fixtures/get-en_jsonapi_field_config_field_config_filter_5Bentity_type_5D_user_filter_5Bbundl-7621df1c6af949cc.json
  • examples/node-client/test/fixtures/get-en_jsonapi_field_storage_config_field_storage_config_filter_5Bentity_type_5D_blo-b4fa915186f31e8c.json
  • examples/node-client/test/fixtures/get-en_jsonapi_field_storage_config_field_storage_config_filter_5Bentity_type_5D_con-ec68571a3424bcd7.json
  • examples/node-client/test/fixtures/get-en_jsonapi_field_storage_config_field_storage_config_filter_5Bentity_type_5D_med-2e49c6455f160b1b.json
  • examples/node-client/test/fixtures/get-en_jsonapi_field_storage_config_field_storage_config_filter_5Bentity_type_5D_nod-806dbaeeaedabb14.json
  • examples/node-client/test/fixtures/get-en_jsonapi_field_storage_config_field_storage_config_filter_5Bentity_type_5D_use-310a174ef92b081d.json
  • examples/node-client/test/fixtures/get-en_jsonapi_node_page_page_5Blimit_5D_2_fields_5Bnode--page_5D_drupal_internal__i-62ebe9a8d27c2692.json
  • examples/node-client/test/fixtures/get-en_jsonapi_node_recipe_page_5Blimit_5D_3_fields_5Bnode--recipe_5D_drupal_interna-0e529d9a26a87f54.json
  • examples/node-client/test/fixtures/get-en_jsonapi_view_view_page_5Blimit_5D_100-d87fbd73cfac9422.json
  • examples/node-client/test/fixtures/get-jsonapi-60b611f3586fb6b8.json
  • examples/node-client/test/fixtures/manifest.json
  • examples/node-client/test/record.js
  • examples/node-client/test/sample.test.js
  • examples/node-client/test/schema.test.js
  • examples/node-client/test/setup.js
  • examples/node-client/test/stubs.test.js
  • examples/node-client/test/types.test.js
  • examples/node-client/test/views.test.js
  • examples/shared/README.md
  • examples/shared/cypress/commands.js
  • examples/shared/cypress/fixtures/umami.json
  • examples/shared/cypress/utils/selectors.js
  • examples/shared/static/druxt-loading.html
  • knip.jsonc
  • package.json
  • packages/druxt/README.md
  • packages/druxt/package.json
  • packages/druxt/src/components/DruxtModule.vue
  • packages/entity/src/components/DruxtEntityForm.vue
  • packages/entity/test/components/DruxtEntityForm.test.js
  • packages/router/src/stores/router.js
  • scripts/check-examples.js

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread examples/druxt-daisyui/pages/index.vue Fixed
@codecov

codecov Bot commented Aug 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 81.50%. Comparing base (5c98a39) to head (469ee6c).

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop     #785      +/-   ##
===========================================
+ Coverage    81.49%   81.50%   +0.01%     
===========================================
  Files          112      112              
  Lines         2853     2855       +2     
  Branches       576      618      +42     
===========================================
+ Hits          2325     2327       +2     
  Misses         436      436              
  Partials        92       92              

see 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

CodeQL flagged the single-pass tag stripping in Recipe Box's summary
(js/incomplete-multi-character-sanitization): nested tags can reform
after one replacement and an unterminated trailing tag survives
entirely. Both strippers now loop until the text stops changing and
match unterminated tags too. Content Ops' word counter had the same
pattern, fixed identically. The rendered output was already Vue-escaped
interpolation in both apps, so this hardens display text rather than
closing a live injection.
…ow CI

All four e2e jobs failed on GitHub runners with 'database is locked'
during tome:install: the parent drush process holds a connection while
its child processes write, SQLite allows one writer, and without a busy
timeout a blocked write fails instead of waiting. PDO::ATTR_TIMEOUT is
pdo_sqlite's busy timeout; 60 seconds outlasts any cache write. The
same provisioning passed on faster disks, which is why it never showed
before this run.
…mode

The busy timeout was not enough: the lock that kills tome:install on
GitHub runners is a deadlock, not slow contention. The parent drush
process holds read locks while its child processes run, rollback-journal
SQLite lets readers block the one writer, and the parent releases
nothing until the child exits - so the child's end-of-request cache
write waits out its timeout and dies as 'database is locked' (the stack
points at DrupalKernel->terminate()). WAL lets readers and the writer
coexist, and the mode persists in the database file, so provision sets
it once right after creating the database and fails loudly if the
pragma does not take.
tome:install spawns child drush processes while the parent stays
bootstrapped, and SQLite allows one writer. On GitHub runners that
combination deadlocks - a child's end-of-request cache write waits out
its busy timeout against the parent's locks and dies as 'database is
locked'. WAL and a 60 second timeout were tried first and verified
insufficient: core enables WAL on every connection already, and the
lock holder outlives any timeout because the parent will not release
until the child it is waiting on exits.

Provision now runs the same steps itself with no process overlap:
site-install, enable tome_sync, clear the profile's own content and
align the site UUID with sync (what tome's prepareConfigForImport does,
and why config:import otherwise refuses to start), config:import twice
(translation collections land on the second pass), then a serial
content import through tome_sync's own importer service inside one
bootstrapped process, ending with the same import-complete event.

Verified locally end to end; the one failing step (consumer cleanup)
fails only against a pre-OAuth config tree that ships no consumers
module, which the branch's config does.
The serial provisioning still deadlocked in CI, one level deeper: when
core.extension changes, drush config:import hands the module install
batch to a nested drush process (drush_backend_batch_process), and that
child dies against its parent's SQLite locks the same way tome:install's
children did. Core's ConfigImporter runs every step synchronously when
driven directly, module installs included, so config now imports through
a php-script and provisioning holds one process end to end.

Verified locally: full provision passes, the language.es collection
lands (frontpage title reads Inicio), and content imports completely.
… run

The e2e specs were written against live-preview verification and CI is
the first environment to execute them. Provisioning now passes and
druxt-daisyui runs green; the rest failed on genuine spec and page bugs:

- druxt-tailwind asserted exact heading text against whitespace-padded
  markup; assert the trimmed text.
- Content Ops called should('have.value') with no argument, which
  compares the value against undefined.
- router-modes demoed /node/1, but nids depend on import order and the
  node does not exist, so two 404 debug blocks rendered their own h1s
  into the page-wide heading assertion. The demo now uses a stable
  alias, and the assertion scopes to the page's own heading.
- menu-edit reused a cy chain stored in a const, so the second loop
  iteration searched for list items inside the first iteration's
  results. Re-query per label.
- reactive-filtering clicked every active chip in one command while
  each toggle re-renders the chip list, failing on detached elements.
  The default pantry is deterministic, so toggle each chip by name.
- druxt-devel-template runs first and absorbs the dev server's initial
  page compile, which exceeds the 4 second default timeout on CI
  runners. The opening query now waits for the compile.
Meal Planner's templates pad their text nodes, so every exact-text
assertion in its spec now compares the trimmed text - the previous fix
caught the heading and this catches the chips and counters.

The start script judged the site on one HEAD request with a 5 second
window, but the first request bootstraps a cold Drupal and a slow CI
runner can take longer - three matrix jobs served fine while test-e2e
lost the coin flip. The check now retries for up to 90 seconds.
The Cypress failure screenshots told both stories directly.

The devel-template spec drove the DruxtDevelTemplate tool through the
search block's missing-template debug UI, but the theming pass added
SearchFormBlockUmami.vue, so that block resolves its wrapper and the
debug UI can never appear again. The spec now pins the resolution
itself - the themed form renders, no debug details - and the tool demo
moves to the pattern examples overhaul with a purpose-built unthemed
block.

router-modes asserted the first h1 on the page, but example routes
render inside the site chrome, whose page_title region contributes an
empty h1 ahead of the page's own. Target the heading by content.
…ounts

The storybook spec's first CI execution failed on an exact option count
(expected 28 blocks, the backend has 24) - counts that pin the demo
data, not the behaviour, and drift with every backend change. The
control assertions now check the list is populated and contains the
option the test selects next, and the unexecuted assertions later in
the same test get the same treatment before they fail the next run.
Counts inside tests that already pass stay exact.
The failure screenshot showed the id control's 24 options render the
block ids as text with no value attributes, so the option[value=...]
existence checks from the previous fix could never match. Assert the
ids appear in the option text instead, which is also what select()
matches on.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants