Skip to content

Commit 42eda89

Browse files
jleifeldcursoragentlasomethingsomething
authored
feat: Add CLI Tool for Creating Meteor Extensions (#678)
* feat: add CLI for creating boilerplate extension * fix: fix pnpm-lock file * fix: enable corepack to fix CI job issues * fix: remove yarn from "create-meteor-extension" scripts * fix: update TS version in create-meteor-extension * feat: optimize the structure of our example app and add variables * chore: update ESLint configuration in create-meteor-extension and run formatting * feat: update create-meteor-extension to enforce 'meteor-app' directory structure for Shopware 6.7+ plugins * feat(create-meteor-extension): improve cli running and example project * ci: add CI integration tests for the create-meteot-extension package * ci: add pull request trigger for create-meteor-extension integration tests * ci: update pnpm version in create-meteor-extension integration workflow * ci: modify plugin creation command to include --no-interaction flag in integration workflow * ci: update plugin creation command in integration workflow to specify plugin namespace * ci: fix path in plugin creation command for integration workflow * ci: update path in plugin creation command for integration workflow * ci: refactor Playwright configuration to use defineConfig for improved clarity * ci: update integration workflow to create admin user and adjust job numbering * ci: update integration workflow to change admin user password for tests * fix: update admin password to meet security requirements in Shopware trunk - Changed admin password from 'shopware' to 'shopware123!' to meet minimum password security requirements introduced in recent Shopware versions - Added cache:clear after password change to ensure changes are applied - Added API endpoint verification step to help diagnose any remaining issues - Updated SHOPWARE_ADMIN_PASSWORD environment variable in test step This fixes the authentication failures in the integration tests where the oauth/token endpoint was not returning an access token. Co-authored-by: Cursor <cursoragent@cursor.com> * fix: remove special character from admin password The exclamation mark in shopware123! was likely causing issues with bash history expansion or YAML parsing. Changed to shopware12345 which still meets minimum password length requirements. Co-authored-by: Cursor <cursoragent@cursor.com> * fix: use default Shopware admin password instead of changing it The setup-shopware action creates admin user with password 'shopware' by default. Removed unnecessary password change step that was causing issues. Using the default password 'shopware' directly in tests. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor: simplify integration tests by removing acceptance-test-suite Replace the complex @shopware-ag/acceptance-test-suite with plain Playwright implementation. This simplifies authentication, reduces dependencies, and makes the tests more maintainable. Changes: - Removed @shopware-ag/acceptance-test-suite dependency - Created simple login helper (helpers/login.ts) - Created API-based product creation helper (helpers/createProduct.ts) - Simplified test fixtures to use plain Playwright - Rewrote all three integration tests with direct DOM interactions - Removed unnecessary API verification step from workflow The new approach uses standard Playwright patterns: - Direct login via form submission - API calls for test data creation - Simple page.goto() for navigation - Standard locator queries for element verification This should resolve the persistent authentication failures. Co-authored-by: Cursor <cursoragent@cursor.com> * fix: use type imports for Playwright Page type The Page type should be imported with 'import type' rather than as a regular import, since it's a TypeScript type and not a runtime export. This fixes the error: SyntaxError: The requested module '@playwright/test' does not provide an export named 'Page' Co-authored-by: Cursor <cursoragent@cursor.com> * feat: add UUID generation function and update product creation logic * ci: enhance integration workflow with cache clearing and extension verification steps * feat: make integreation test working for create-meteor-extension cli * format: run formatter * fix: update createProduct function signature in integration tests * docs: update development instructions for create-meteor-extension * fix: apply code review comments * feat: introduce a new command `test:nightly` for running tests specifically on template dependencies * fix: fix pnpm-lock file * fix: update test:nightly command to do an NPM audit with a real NPM command --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: somethings <l.apple@shopware.com>
1 parent eb968bd commit 42eda89

45 files changed

Lines changed: 3216 additions & 214 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
name: create-meteor-extension Integration Tests
2+
3+
on:
4+
pull_request:
5+
6+
push:
7+
branches:
8+
- main
9+
workflow_dispatch:
10+
11+
jobs:
12+
integration-test:
13+
name: Test CLI with Shopware trunk
14+
runs-on: ubuntu-latest
15+
timeout-minutes: 30
16+
17+
steps:
18+
# 1. Setup Shopware 6 using shopware/setup-shopware
19+
- name: Setup Shopware
20+
uses: shopware/setup-shopware@v2
21+
with:
22+
shopware-version: trunk
23+
shopware-repository: shopware/shopware
24+
install: true
25+
install-locale: en-GB
26+
install-currency: EUR
27+
install-admin: true
28+
env: dev
29+
path: shopware/
30+
31+
# 2. Checkout meteor repo
32+
- name: Checkout meteor
33+
uses: actions/checkout@v6
34+
with:
35+
path: meteor/
36+
37+
# 3. Setup Node.js and pnpm
38+
- uses: pnpm/action-setup@v4
39+
with:
40+
version: 10.12.3
41+
42+
- uses: actions/setup-node@v6
43+
with:
44+
node-version: '24'
45+
cache: 'pnpm'
46+
cache-dependency-path: 'meteor/pnpm-lock.yaml'
47+
48+
# 4. Create plugin structure using Shopware's plugin:create command
49+
- name: Create plugin using Shopware CLI
50+
working-directory: shopware/
51+
run: |
52+
bin/console plugin:create TestExtensionPlugin Test --no-interaction
53+
54+
# 5. Install meteor dependencies and build CLI
55+
- name: Install meteor dependencies
56+
working-directory: meteor/
57+
run: pnpm install --frozen-lockfile
58+
59+
- name: Build create-meteor-extension CLI
60+
working-directory: meteor/
61+
run: pnpm turbo run build --filter=@shopware-ag/create-meteor-extension
62+
63+
# 6. Generate extension using CLI directly into plugin structure
64+
- name: Generate meteor extension into plugin
65+
working-directory: shopware/custom/plugins/TestExtensionPlugin/src/Resources/app
66+
run: |
67+
node ../../../../../../../meteor/packages/create-meteor-extension/bin/create-meteor-extension --name test-extension --output-dir meteor-app
68+
69+
# 7. Install generated extension dependencies
70+
- name: Install extension dependencies
71+
working-directory: shopware/custom/plugins/TestExtensionPlugin/src/Resources/app/meteor-app
72+
run: pnpm install
73+
74+
# 8. Build extension
75+
- name: Build extension
76+
working-directory: shopware/custom/plugins/TestExtensionPlugin/src/Resources/app/meteor-app
77+
run: pnpm run build
78+
79+
# 9. Activate plugin in Shopware
80+
- name: Activate plugin
81+
working-directory: shopware/
82+
run: |
83+
bin/console plugin:refresh
84+
bin/console plugin:install --activate TestExtensionPlugin
85+
bin/console cache:clear
86+
87+
# 10. Build Shopware admin assets
88+
- name: Build Shopware admin
89+
working-directory: shopware/
90+
run: |
91+
composer run build:js:admin
92+
bin/console cache:clear
93+
94+
# 11. Verify extension files exist
95+
- name: Verify extension build
96+
run: |
97+
echo "Checking extension files..."
98+
ls -la shopware/custom/plugins/TestExtensionPlugin/src/Resources/app/meteor-app/dist/
99+
echo "Checking for extension in public..."
100+
ls -la shopware/public/bundles/testextensionplugin/
101+
102+
# 12. Start Shopware web server
103+
- name: Start Shopware server
104+
working-directory: shopware/
105+
run: |
106+
symfony server:start --allow-http --no-tls --port=8000 -d
107+
108+
# 13. Wait for server to be ready
109+
- name: Wait for Shopware to be ready
110+
run: |
111+
timeout 60 bash -c 'until curl -f http://localhost:8000/admin; do sleep 2; done'
112+
113+
# 14. Install Playwright and run tests
114+
- name: Install integration test dependencies
115+
working-directory: meteor/packages/create-meteor-extension/tests/integration
116+
run: |
117+
pnpm install
118+
npx playwright install --with-deps chromium
119+
120+
- name: Run Playwright tests
121+
working-directory: meteor/packages/create-meteor-extension/tests/integration
122+
env:
123+
APP_URL: http://localhost:8000
124+
SHOPWARE_ADMIN_USERNAME: admin
125+
SHOPWARE_ADMIN_PASSWORD: shopware
126+
CI: true
127+
run: npx playwright test --reporter=github --trace=on-first-retry
128+
129+
# 15. Upload test results
130+
- uses: actions/upload-artifact@v6
131+
if: always()
132+
with:
133+
name: integration-test-results
134+
path: meteor/packages/create-meteor-extension/tests/integration/test-results/
135+
retention-days: 7
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: create-meteor-extension Nightly Checks
2+
3+
on:
4+
schedule:
5+
- cron: "0 2 * * *" # Every night at 2am UTC
6+
workflow_dispatch:
7+
8+
jobs:
9+
template-dependency-audit:
10+
name: Check template dependencies for outdated major versions
11+
runs-on: ubuntu-latest
12+
continue-on-error: true
13+
14+
steps:
15+
- name: Checkout
16+
uses: actions/checkout@v6
17+
18+
- uses: pnpm/action-setup@v4
19+
20+
- uses: actions/setup-node@v6
21+
with:
22+
node-version: "24"
23+
cache: "pnpm"
24+
25+
- name: Install dependencies
26+
run: pnpm install --frozen-lockfile --prefer-offline
27+
28+
- name: Run nightly template dependency check
29+
run: pnpm --filter @shopware-ag/create-meteor-extension run test:nightly
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
.DS_Store
2+
node_modules
3+
npm-debug.log
4+
coverage
5+
.nyc_output
6+
dist
7+
build
8+
.vscode
9+
10+
# Generated extension output
11+
meteor-app/
12+
13+
# Integration test artifacts
14+
tests/integration/test-results/
15+
tests/integration/node_modules/
16+
tests/integration/package-lock.json
17+
tests/integration/pnpm-lock.yaml
18+
19+
# Test temporary directories
20+
__tests__/tmp/
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2017
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import { filesystem } from 'gluegun'
2+
3+
const src = filesystem.path(__dirname, '..')
4+
5+
test('outputs version', async () => {
6+
const { run } = require(filesystem.path(src, 'src', 'cli'))
7+
const toolbox = await run(['--version'])
8+
9+
expect(toolbox).toBeDefined()
10+
expect(run).toBeDefined()
11+
})
12+
13+
test('outputs help', async () => {
14+
const { run } = require(filesystem.path(src, 'src', 'cli'))
15+
const toolbox = await run(['--help'])
16+
17+
expect(toolbox).toBeDefined()
18+
expect(run).toBeDefined()
19+
})
20+
21+
test('validates extension name - rejects uppercase', async () => {
22+
// TODO: This test would need proper mocking of the prompt
23+
// For now, we're just validating the regex pattern
24+
const regex = /^[a-z0-9-]+$/
25+
expect(regex.test('TestExtension')).toBe(false)
26+
expect(regex.test('test-extension')).toBe(true)
27+
})
28+
29+
test('validates extension name - rejects spaces', async () => {
30+
const regex = /^[a-z0-9-]+$/
31+
expect(regex.test('test extension')).toBe(false)
32+
expect(regex.test('test-extension')).toBe(true)
33+
})
34+
35+
test('validates extension name - accepts valid names', async () => {
36+
const regex = /^[a-z0-9-]+$/
37+
expect(regex.test('my-extension')).toBe(true)
38+
expect(regex.test('test123')).toBe(true)
39+
expect(regex.test('my-extension-2024')).toBe(true)
40+
})
41+
42+
test('non-interactive mode - creates extension package.json with correct name', async () => {
43+
const { run } = require(filesystem.path(src, 'src', 'cli'))
44+
const testName = 'test-extension-' + Date.now() + '-' + Math.random().toString(36).slice(2, 8)
45+
const testDir = filesystem.path(src, '__tests__', 'tmp', testName)
46+
47+
// Ensure test directory doesn't exist
48+
if (filesystem.exists(testDir)) {
49+
filesystem.remove(testDir)
50+
}
51+
52+
// Create temp directory and run CLI in non-interactive mode
53+
filesystem.dir(testDir)
54+
55+
// Change to test directory
56+
const originalCwd = process.cwd()
57+
process.chdir(testDir)
58+
59+
try {
60+
const toolbox = await run(['--name', testName])
61+
62+
// Check that meteor-app directory was created
63+
const meteorAppDir = filesystem.path(testDir, 'meteor-app')
64+
expect(filesystem.exists(meteorAppDir)).toBe(true)
65+
66+
// Check that package.json was created with correct name
67+
const packageJson = filesystem.read(
68+
filesystem.path(meteorAppDir, 'package.json'),
69+
'json',
70+
)
71+
expect(packageJson.name).toBe(testName)
72+
} finally {
73+
// Clean up
74+
process.chdir(originalCwd)
75+
if (filesystem.exists(testDir)) {
76+
filesystem.remove(testDir)
77+
}
78+
}
79+
})
80+
81+
test('non-interactive mode - creates extension with custom --output-dir', async () => {
82+
const { run } = require(filesystem.path(src, 'src', 'cli'))
83+
const testName = 'custom-output-' + Date.now() + '-' + Math.random().toString(36).slice(2, 8)
84+
const testDir = filesystem.path(src, '__tests__', 'tmp', testName)
85+
const customOutputDir = 'my-custom-app'
86+
87+
// Ensure test directory doesn't exist
88+
if (filesystem.exists(testDir)) {
89+
filesystem.remove(testDir)
90+
}
91+
92+
// Create temp directory and run CLI in non-interactive mode
93+
filesystem.dir(testDir)
94+
95+
// Change to test directory
96+
const originalCwd = process.cwd()
97+
process.chdir(testDir)
98+
99+
try {
100+
const toolbox = await run([
101+
'--name',
102+
testName,
103+
'--output-dir',
104+
customOutputDir,
105+
])
106+
107+
// Check that custom output directory was created
108+
const customDir = filesystem.path(testDir, customOutputDir)
109+
expect(filesystem.exists(customDir)).toBe(true)
110+
111+
// Check that package.json was created with correct name
112+
const packageJson = filesystem.read(
113+
filesystem.path(customDir, 'package.json'),
114+
'json',
115+
)
116+
expect(packageJson.name).toBe(testName)
117+
} finally {
118+
// Clean up
119+
process.chdir(originalCwd)
120+
if (filesystem.exists(testDir)) {
121+
filesystem.remove(testDir)
122+
}
123+
}
124+
})

0 commit comments

Comments
 (0)