Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions plugin-tests/dev-tooling/fixtures/shopware-trunk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Shopware Trunk Fixtures

## 🔖 Source

The `scripts` blocks of two `package.json` files from [shopware/shopware](https://github.com/shopware/shopware), commit `c59a7d3484c`:

| Fixture | Mirrors |
|---------------------------------------|----------------------------------------------------------------|
| `administration-package-scripts.json` | `src/Administration/Resources/app/administration/package.json` |
| `storefront-package-scripts.json` | `src/Storefront/Resources/app/storefront/package.json` |

Only the `scripts` block is kept, in the key order of the source. The JS tool suites answer `npm pkg get "scripts.<name>"` probes from these files through `shopware_trunk_script_body` in `test_helper/common_setup.bash`, so a route naming a script trunk does not define fails as it would on a real checkout.

## ♻️ Regeneration

```bash
./regenerate.sh /path/to/shopware [output-dir]
```

The script reads the checkout and writes both files into `output-dir`, by default this directory. Check out the commit named above first to reproduce the committed files; a newer trunk can change the scripts.
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"build": "export VITE_MODE=production && ts-node -T build.ts",
"code-mods": "node code-mods.js",
"codemod:js-vue3-feature-flag-removal": "ts-node --transpileOnly ./scripts/codemods/js-vue3-feature-flag-removal/index.ts",
"codemod:twig-feature-flag-removal": "ts-node --transpileOnly ./scripts/codemods/twig-feature-flag-removal/index.ts",
"codemod:twig-remove-blocks": "ts-node --transpileOnly ./scripts/codemods/twig-block-removal/index.ts",
"codemod:sfc-migration": "ts-node --transpileOnly ./scripts/codemods/sfc-migration/run-sfc-migration.ts",
"convert-entity-schema": "ts-node --transpileOnly ./scripts/entitySchemaConverter/convert-schema.ts",
"create-test": "node ./scripts/create-spec-file/index.mjs",
"dev": "export VITE_MODE=development && ts-node -T build.ts",
"extension-tooling:check": "ts-node --transpileOnly ./scripts/extensionTooling/check.ts",
"extension-tooling:setup": "ts-node --transpileOnly ./scripts/extensionTooling/setup.ts",
"format": "prettier --check \"src/**/*.{js,ts}\" \"test/**/*.{js,ts}\" \"extension-tooling/**/*.mjs\" \"scripts/extensionTooling/**/*.ts\" \"scripts/codemods/**/*.ts\" \"scripts/generate-shopware-modules/**/*.ts\" \"build/vite-plugins/**/*.{js,ts}\" \"build/vue-setup-transform/**/*.{js,ts}\" \"build/plugins.vite.ts\" \"build.ts\"",
"format:fix": "prettier --write \"src/**/*.{js,ts}\" \"test/**/*.{js,ts}\" \"extension-tooling/**/*.mjs\" \"scripts/extensionTooling/**/*.ts\" \"scripts/codemods/**/*.ts\" \"scripts/generate-shopware-modules/**/*.ts\" \"build/vite-plugins/**/*.{js,ts}\" \"build/vue-setup-transform/**/*.{js,ts}\" \"build/plugins.vite.ts\" \"build.ts\" --cache",
"generate-api-docs": "./node_modules/.bin/jsdoc src -r -c jsdoc.config.js",
"generate-component-import-resolver-map": "ts-node --transpileOnly ./scripts/componentImportResolver/generate.ts",
"generate-data-set-list": "ts-node --transpileOnly ./scripts/generate-data-set-list/index.ts",
"generate-blocks-list": "ts-node --transpileOnly ./scripts/generate-block-list/index.ts",
"generate-entity-schema-package": "ts-node --transpileOnly ./scripts/entitySchemaConverter/generate-package.ts",
"generate-position-identifier-list": "ts-node --transpileOnly ./scripts/generate-position-identifier-list/index.ts",
"generate-shopware-modules": "ts-node --transpileOnly ./scripts/generate-shopware-modules/index.ts",
"preinstall": "node ./scripts/validate-package-json/index.mjs",
"postinstall": "patch-package",
"lint": "eslint src test build/vue-setup-transform build/vite-plugins/virtual-shopware-modules extension-tooling scripts/extensionTooling scripts/generate-shopware-modules scripts/codemods build.ts --cache",
"lint:all": "npm run lint:types && npm run lint && npm run lint:scss && npm run format",
"lint:debugging": "eslint",
"lint:deprecations": "FILES=$(git status -s | awk '/(js|ts|vue|html\\.twig)$/ {print $2}'); [ -z \"$FILES\" ] || eslint --rule '\"sw-deprecation-rules/private-feature-declarations\": \"error\"' $FILES",
"lint:fix": "npm run lint -- --fix",
"lint:junit": "eslint --format junit src test build/vite-plugins/virtual-shopware-modules extension-tooling scripts/extensionTooling scripts/generate-shopware-modules scripts/codemods build.ts > eslint.junit.xml",
"lint:scss": "stylelint **/*.scss --cache",
"lint:scss-fix": "npm run lint:scss -- --fix",
"lint:snippets": "eslint \"src/**/snippet/*.json\" --no-cache",
"lint:snippets:all": "SNIPPET_ALLOW_LIST_DISABLED=1 eslint \"src/**/snippet/*.json\" --no-cache",
"lint:test-parser": "eslint test/eslint/error-reference.html.twig --no-ignore",
"lint:twig": "eslint src",
"lint:types": "tsc",
"lint-fix": "eslint --fix src test build/vite-plugins/virtual-shopware-modules extension-tooling scripts/extensionTooling scripts/generate-shopware-modules scripts/codemods build.ts",
"security-check": "./node_modules/.bin/nsp check",
"test-debug": "node --inspect-brk TZ=UTC node_modules/.bin/jest --watch --config jest.config.js --runInBand --no-cache",
"unit": "jest --config jest.config.js --ci",
"unit:extension-tooling-e2e": "EXTENSION_TOOLING_E2E=1 jest --config jest.config.js --ci --coverage=false scripts/extensionTooling/e2e.spec",
"unit-setup": "npm run generate-component-import-resolver-map",
"unit-watch": "jest --config jest.config.js --watch",
"vite": "vite",
"vite:build": "vite build",
"perf": "lhci autorun --config=./lighthouserc.js",
"perf:collect": "lhci collect --config=./lighthouserc.js",
"perf:assert": "lhci assert --config=./lighthouserc.js"
}
30 changes: 30 additions & 0 deletions plugin-tests/dev-tooling/fixtures/shopware-trunk/regenerate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
# Rewrites the two package-scripts fixtures from a shopware/shopware checkout.
# Usage: regenerate.sh <shopware-checkout> [output-dir]
# The output directory defaults to the directory of this script. The checkout
# is only read.
set -euo pipefail

if [[ $# -lt 1 || $# -gt 2 ]]; then
printf 'Usage: %s <shopware-checkout> [output-dir]\n' "${0##*/}" >&2
exit 64
fi

checkout="$1"
output_dir="${2:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)}"

# fixture file name -> package.json below the checkout
declare -A sources=(
[administration-package-scripts.json]="src/Administration/Resources/app/administration/package.json"
[storefront-package-scripts.json]="src/Storefront/Resources/app/storefront/package.json"
)

for fixture in "${!sources[@]}"; do
package_json="${checkout}/${sources[${fixture}]}"
if [[ ! -f "${package_json}" ]]; then
printf 'No package.json at %s\n' "${package_json}" >&2
exit 66
fi
# No -S: the fixtures keep the key order of the package.json.
jq '.scripts' "${package_json}" > "${output_dir}/${fixture}"
done
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"build:shopware": "vite build --config vite.shopware.config.mts",
"build:components": "node build/vite/build-components.js",
"dev:server": "vite --config vite.components.config.mts",
"development": "NODE_ENV=development webpack --config webpack.config.js",
"hot": "NODE_ENV=development MODE=hot webpack-dev-server --config webpack.config.js",
"hot-proxy": "NODE_ENV=development MODE=hot node ./build/start-hot-reload.js",
"lint": "eslint --no-error-on-unmatched-pattern --report-unused-disable-directives ./src --cache && stylelint --config stylelint.config.js ./src/scss --cache",
"lint-fix": "npm run lint:js:fix && npm run lint:scss-fix",
"lint:js": "eslint --no-error-on-unmatched-pattern --report-unused-disable-directives ./src ./build && (cd ../.. && eslint --no-error-on-unmatched-pattern --config ./app/storefront/eslint.config.js --report-unused-disable-directives views/components)",
"lint:js:fix": "eslint --no-error-on-unmatched-pattern --fix --report-unused-disable-directives ./src ./build && (cd ../.. && eslint --no-error-on-unmatched-pattern --fix --config ./app/storefront/eslint.config.js --report-unused-disable-directives views/components)",
"lint:scss": "stylelint --config stylelint.config.js ./src/scss --cache",
"lint:scss-fix": "npm run lint:scss -- --fix",
"production": "NODE_ENV=production webpack --config webpack.config.js && npm run build:shopware && npm run build:components",
"unit": "jest --config jest.config.js --ci",
"unit-watch": "jest --config jest.config.js --watch",
"unit:components": "vitest run --config vitest.config.mts",
"unit:components:watch": "vitest --config vitest.config.mts",
"unit:components:coverage": "vitest run --coverage --config vitest.config.mts",
"postinstall": "patch-package && node build/link-component-node-modules.js",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
}
103 changes: 98 additions & 5 deletions plugin-tests/dev-tooling/js_admin_tools.bats
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ bats_test_function --description "blocks npx eslint → suggests eslint_check" \
bats_test_function --description "blocks npx jest → suggests jest_run" \
-- js_admin_hook_blocks "npx jest --watch" "jest_run"

# The target-less base scripts the MCP tools route path-scoped runs at. None of
# them matched a pattern before: `:` is not in the `(\s|--|$)` tail the shorter
# names use, so `npm run lint` never covered `npm run lint:debugging`.
# The target-less base scripts. The MCP tools call lint:debugging (Admin ESLint)
# and jest:base; Shopware defines none of the others, and the redirects stay for
# a project that does.
# bats test_tags=blocking,base-scripts
bats_test_function --description "blocks npm run lint:debugging → suggests eslint_check" \
-- js_admin_hook_blocks "npm run lint:debugging" "eslint_check"
Expand All @@ -77,14 +77,78 @@ bats_test_function --description "blocks npm run prettier:base → suggests pret
bats_test_function --description "blocks npm run jest:base → suggests jest_run" \
-- js_admin_hook_blocks "npm run jest:base" "jest_run"

# A linter binary run through `npx`, `npm exec` or its node_modules/.bin path.
# The MCP servers run the binaries themselves through `npm exec --no --`, so an
# agent that copies that form from a server log reaches the hook with it.
# bats test_tags=blocking,binaries
for _entry in eslint:eslint_check stylelint:stylelint_check prettier:prettier_check \
jest:jest_run tsc:tsc_check; do
_binary="${_entry%%:*}"
_tool="${_entry##*:}"
for _form in "${JS_HOOK_BINARY_FORMS[@]}"; do
_command="${_form//BIN/${_binary}} src/module"
bats_test_function --description "blocks ${_command} → suggests ${_tool}" \
-- js_admin_hook_blocks "${_command}" "${_tool}"
done
bats_test_function --description "blocks ${_binary} run through npm exec in the Administration tree → suggests ${_tool}" \
-- js_admin_hook_blocks \
"cd src/Administration/Resources/app/administration && npm exec --no -- ${_binary} src/module" "${_tool}"
done
unset _entry _binary _tool _form _command

# The binary forms follow the same context rule as the scripts: a command that
# names the Storefront tree belongs to the Storefront hook.
# bats test_tags=context,allow
@test "allows npm run stylelint:app, which only the Storefront package declares" {
admin_hook_allows_storefront_binary() {
run_hook "check-js-admin-tools.sh" "cd src/Storefront/Resources/app/storefront && npm exec --no -- $1 src"
assert_success
}
bats_test_function --description "allows npm exec eslint when the command names the Storefront tree" \
-- admin_hook_allows_storefront_binary eslint
bats_test_function --description "allows npm exec stylelint when the command names the Storefront tree" \
-- admin_hook_allows_storefront_binary stylelint
bats_test_function --description "allows npm exec prettier when the command names the Storefront tree" \
-- admin_hook_allows_storefront_binary prettier
bats_test_function --description "allows npm exec jest when the command names the Storefront tree" \
-- admin_hook_allows_storefront_binary jest
bats_test_function --description "allows npm exec tsc when the command names the Storefront tree" \
-- admin_hook_allows_storefront_binary tsc

# `npx` and `npm exec` run other packages too, and a binary's name turns up in
# arguments and quoted text. None of these invokes a linter.
# bats test_tags=allow
admin_hook_allows() {
run_hook "check-js-admin-tools.sh" "$1"
assert_success
}
bats_test_function --description "allows npm exec vitest, which the Administration server has no tool for" \
-- admin_hook_allows "npm exec -- vitest src"
bats_test_function --description "allows npm exec running another tool" \
-- admin_hook_allows "npm exec -- some-other-tool src"
# `--package` takes a value, so the linter name after it is not the command.
bats_test_function --description "allows npm exec --package naming a linter but running another tool" \
-- admin_hook_allows "npm exec --package eslint some-other-tool"
bats_test_function --description "allows npx running another tool" \
-- admin_hook_allows "npx --yes some-other-tool src"
bats_test_function --description "allows a linter name after another tool in an npx command" \
-- admin_hook_allows "npx --yes some-other-tool eslint"
bats_test_function --description "allows npm exec running a package whose name starts with eslint" \
-- admin_hook_allows "npm exec -- eslint-plugin-foo"
bats_test_function --description "allows installing a linter" \
-- admin_hook_allows "npm install eslint"
bats_test_function --description "allows reading a linter binary" \
-- admin_hook_allows "cat node_modules/.bin/eslint"
bats_test_function --description "allows a linter name inside quoted text after a semicolon" \
-- admin_hook_allows 'git commit -m "tidy; eslint config"'

# bats test_tags=context,allow
@test "allows npm run stylelint:app, a Storefront script name" {
run_hook "check-js-admin-tools.sh" "npm run stylelint:app"
assert_success
}

# bats test_tags=context,allow
@test "allows npm run eslint:app, which only the Storefront package declares" {
@test "allows npm run eslint:app, a Storefront script name" {
run_hook "check-js-admin-tools.sh" "npm run eslint:app"
assert_success
}
Expand All @@ -107,3 +171,32 @@ bats_test_function --description "blocks npm run jest:base → suggests jest_run
run_hook "check-js-admin-tools.sh" "npm run lint"
assert_success
}

# `npm x` is the alias of `npm exec`; the -c / --call form runs a command
# string, and a binary at a command position inside it is blocked.
# bats test_tags=binary,blocking
for spec in "eslint eslint_check" "stylelint stylelint_check" "prettier prettier_check" "jest jest_run" "tsc tsc_check"; do
read -r bin tool <<<"$spec"
bats_test_function --description "blocks npm x running ${bin}" \
-- js_admin_hook_blocks "npm x -- ${bin} src" "$tool"
bats_test_function --description "blocks npm exec -c running ${bin} after cd" \
-- js_admin_hook_blocks "npm exec --no -c \"cd ../.. && ${bin} src\"" "$tool"
done
bats_test_function --description "blocks the npm exec -c form the MCP tools run for the components tree" \
-- js_admin_hook_blocks 'npm exec --no -c "cd ../.. && eslint views/components"' "eslint_check"
bats_test_function --description "blocks npx -c with a single-quoted string running stylelint" \
-- js_admin_hook_blocks "npx -c 'stylelint src'" "stylelint_check"
bats_test_function --description "blocks npm exec --call running jest" \
-- js_admin_hook_blocks 'npm exec --call "jest src"' "jest_run"
bats_test_function --description "blocks npm exec -c after --package with its value" \
-- js_admin_hook_blocks 'npm exec --package foo -c "eslint src"' "eslint_check"
bats_test_function --description "blocks npm exec -c with leading blanks in the string" \
-- js_admin_hook_blocks 'npm exec -c " eslint src"' "eslint_check"
bats_test_function --description "blocks npm exec -c with a variable assignment before the binary" \
-- js_admin_hook_blocks 'npm exec -c "NODE_ENV=test jest src"' "jest_run"

# bats test_tags=binary,allow
bats_test_function --description "allows npm exec -c naming a linter as an argument" \
-- admin_hook_allows 'npm exec -c "echo eslint"'
bats_test_function --description "allows npm x running another tool" \
-- admin_hook_allows "npm x -- some-other-tool src"
Loading
Loading