@friendsofshopware/vitest-shopware-admin-bridge provides a Vitest and Vue Test
Utils environment for Shopware 6 Administration extensions. It boots the real
installed Administration runtime while keeping version-sensitive setup out of
each extension.
The bridge currently supports Shopware 6.6 and 6.7. It also detects and uses the native Shopware setup-SFC transform when a newer Administration source tree ships it.
- Set up a new Shopware plugin covers the local file layout, first tests, npm lockfiles, a Shopware-version CI matrix and an optional release-artifact job.
- Testing best practices explains which level of test to choose, how to isolate Shopware state and which helpers belong in an extension rather than the bridge.
- Shopware testing inventory records the upstream testing patterns and the bridge's compatibility boundary.
- Shopware 6.6 or 6.7 Administration source and installed npm dependencies
- Node.js 22.12+, 24, or 26+
- npm 10+
- Vitest 5 and Vite 6.4+
The bridge discovers an Administration in a Shopware monorepo, a Composer
installation, or an npm administration link. SHOPWARE_ADMINISTRATION_PATH
and the legacy ADMIN_PATH can override discovery. Discovery is read-only: the
bridge never downloads or modifies Shopware.
Install the published package and commit the resulting npm lockfile. See the complete plugin setup guide for Administration dependency installation and CI:
npm install --save-dev vitest@5 @friendsofshopware/vitest-shopware-admin-bridge// vitest.config.ts
import { defineShopwareConfig } from '@friendsofshopware/vitest-shopware-admin-bridge';
export default defineShopwareConfig();{
"scripts": {
"test:unit": "vitest run",
"test:unit:watch": "vitest"
}
}Tests are discovered under src/ and test/ using .spec.* or .test.*.
Consumer options are merged without allowing the mandatory jsdom bootstrap to
be replaced:
export default defineShopwareConfig({
administrationPath: process.env.SHOPWARE_ADMINISTRATION_PATH,
runtime: {
strictConsole: true,
},
vitest: {
test: {
include: ['tests/unit/**/*.spec.ts'],
},
},
});The configuration installs:
- the correct 6.6 Vue compatibility or 6.7 native Vue runtime;
- Vue SFC compilation and the Administration-native setup transform when present;
- Twig, SCSS/CSS/Less and SVG handling;
- real Shopware component, mixin, directive, filter, state and service registries;
- the Administration Pinia store initializer plus context and a safe no-op notification store on 6.7, and legacy Vuex state on 6.6;
- fresh Pinia and legacy Vuex state for every test;
- Vue Test Utils plugins, dependency injection, synchronized locales/i18n and standard Administration mocks.
The full runtime also installs slot-preserving stubs for common layout
components (sw-page, sw-card-view, mt-card, mt-banner,
mt-empty-state, mt-link and sw-search-bar) plus lightweight icon stubs.
Named sw-page content and smart-bar slots therefore remain visible without a
local page-stub map.
Import the extension entry point so it registers its components, then mount by Shopware component name:
import { expect, it } from 'vitest';
import { mountShopwareComponent } from '@friendsofshopware/vitest-shopware-admin-bridge/test-utils';
import '../src/main';
it('renders the widget', async () => {
const wrapper = await mountShopwareComponent('acme-widget', {
props: { title: 'Orders' },
});
expect(wrapper.text()).toContain('Orders');
});loadShopwareComponent() scans the installed Administration source without
writing Shopware's private generated component map. This allows extensions to
test overrides and extended core components even when their base component was
not registered yet. Dependencies are loaded recursively before
Shopware.Component.build() applies the override chain.
The resulting import map is cached in the operating system's temporary directory per Administration Git revision. Disable the cache while editing the selected Administration source without changing its revision:
export default defineShopwareConfig({
runtime: { componentScanCache: false },
});VITEST_SHOPWARE_ADMIN_BRIDGE_CACHE_DIR can place the cache below a different
base directory. Cache read and write failures fall back to a fresh scan.
Shopware.Component.override('sw-button', overrideConfig);
await loadShopwareComponent('sw-button');
const component = await buildShopwareComponent('sw-button');Available component utilities:
loadShopwareComponent(name)buildShopwareComponent(name)mountShopwareComponent(name, options)shallowMountShopwareComponent(name, options)- direct Vue Test Utils exports:
mount,shallowMount,flushPromises,config
Default service mocks reset before every test. ACL allows access by default, feature flags are inactive, user configuration is empty, and repository calls return empty results.
import {
getShopwareTestServices,
mockShopwareService,
setAclRoles,
setFeatureFlags,
withShopwareService,
} from '@friendsofshopware/vitest-shopware-admin-bridge/test-utils';
setAclRoles(['order.viewer']);
setFeatureFlags(['FEATURE_NEXT']);
const restore = mockShopwareService('myService', { load: vi.fn() });
restore();
await withShopwareService('myService', replacement, async () => {
// Shopware.Service('myService') and inject('myService') use replacement.
});
getShopwareTestServices().userConfigService.search.mockResolvedValue({ data: {} });Scoped service helpers update both Shopware's Bottle container and Vue dependency injection, then restore the exact previous instance. Any un-restored scope is cleaned up automatically after the test.
The bridge provides small DAL-shaped doubles rather than Shopware's private HTTP/entity-schema test infrastructure:
const products = createRepositoryMock({
search: vi.fn().mockResolvedValue({
data: [{ id: 'product-1' }],
total: 1,
}),
});
setRepositoryMocks({ product: products });An unconfigured entity name throws immediately instead of silently returning the wrong fixture.
setShopwareContext() updates Shopware.Context.api and the real
context/session/system stores. The bridge boots the Pinia stores used by
Shopware 6.7 and keeps the legacy Vuex stores working on 6.6:
setShopwareContext({
api: { languageId: 'language-id' },
session: { locale: 'de-DE', locales: ['de-DE'], languageId: 'language-id' },
});The standard en-GB and de-DE locales are registered before extension code
runs. Locale messages added through Shopware's locale factory are synchronized
to the Vue i18n instance before mountShopwareComponent() and
shallowMountShopwareComponent() mount a component.
State is reset automatically. resetShopwareTestState() is available when a
single test needs to return to the default API context, locale state, services
and a fresh Pinia explicitly.
Class, API-client and other non-component tests can use a lighter runtime that keeps Shopware context, state and service helpers but skips Administration component discovery, mixins, directives, filters and component-helper registration:
export default defineShopwareConfig({
runtime: { mode: 'lite' },
vitest: {
test: { include: ['test/core/**/*.spec.ts'] },
},
});Use a separate Vitest config when one package needs both modes. Tests that
mount registered Shopware components must continue to use the default
mode: 'full' runtime.
The bridge installs these methods on every Vue wrapper and also exports them as standalone functions:
findByText(selector, text)findByAriaLabel(selector, text)findByLabel(text)findByPlaceholder(text)
selectMtSelectOptionByText(wrapper, text) covers the recurring Meteor select
interaction while accepting either the legacy popover or role-based option
markup.
Enable runtime.strictConsole to fail tests that emit unexpected
console.warn or console.error. Permit deliberate output in the test that
owns it:
allowConsoleMessage('expected deprecation', 'warn');Strict mode starts after the Shopware bootstrap, so it reports test behavior rather than initialization noise.
The bridge deliberately separates stable test adapters from Shopware runtime behavior.
Bridge-owned adapters behave identically for every supported Administration:
- Twig and imported HTML become ESM strings; HTML and Twig comments are removed while all other bytes are preserved;
- SVG imports contain the exact UTF-8 file contents;
- CSS, SCSS and Less imports resolve to an empty module in unit tests;
- jsdom polyfills, repository doubles, queries and console enforcement use the bridge's own versioned contracts.
The selected Administration remains authoritative for Vue or Vue compat, Shopware's component and Twig runtime factories, state, services, mixins, directives, filters, core components and native setup-SFC transformation. This keeps the test-facing API stable without making a Shopware 6.6 test silently run copied 6.7 runtime behavior. The bridge does not import Shopware's private Twig, SVG or style Vite plugins.
npx vitest-shopware-admin-bridge doctor
npx vitest-shopware-admin-bridge doctor --json
npx vitest-shopware-admin-bridge doctor --admin-path /path/to/administrationIf dependencies are missing, the command prints the exact npm ci --prefix ...
command. It does not run the command automatically.
See docs/testing-inventory.md for the Shopware test pattern inventory, helper ownership decisions, gap-closure table and executable proof for each compatibility behavior.