@webship.co/workspace-joomla 0.0.0-stage → 1.0.8
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 +197 -2
- package/ai/agents/joomla-site-builder.md +113 -0
- package/ai/agents/joomla-tester.md +138 -0
- package/ai/commands/joomla-test.md +91 -0
- package/ai/prompts/joomla-build-and-test.md +27 -0
- package/ai/skills/joomla-scenarios/SKILL.md +230 -0
- package/ai/skills/joomla-scenarios/examples/article-lifecycle.feature +36 -0
- package/ai/skills/joomla-scenarios/examples/groups-and-pages.feature +41 -0
- package/ai/skills/joomla-scenarios/examples/site-and-search.feature +33 -0
- package/commands/cmd-joomla-project.sh +33 -0
- package/commands/cmd-tools-joomla-cli.sh +26 -0
- package/dist/src/index.d.ts +30 -0
- package/dist/src/index.js +51 -0
- package/dist/src/index.js.map +1 -0
- package/docs/plugin-items-pattern.md +150 -0
- package/features/joomla.feature +30 -0
- package/package.json +95 -4
- package/scripts/args/arg-joomla-cli.sh +25 -0
- package/scripts/args/arg-joomla.sh +29 -0
- package/scripts/functions/fun-joomla.sh +387 -0
- package/testing/site.feature +64 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
Feature: The Joomla plugin on the dashboard
|
|
2
|
+
Once the plugin is activated and installed, the sites folders offer the Joomla builder, each
|
|
3
|
+
AI folder lists the items of its kind the plugin ships, and the Plugins page lists the plugin
|
|
4
|
+
with its steps.
|
|
5
|
+
|
|
6
|
+
Scenario: The plugin is listed on the Plugins page
|
|
7
|
+
Given I am on "/settings/plugins"
|
|
8
|
+
Then I should see "Joomla"
|
|
9
|
+
And I should see "@webship.co/workspace-joomla"
|
|
10
|
+
|
|
11
|
+
Scenario: A sites folder offers the Joomla builder
|
|
12
|
+
Given I am on "/dev"
|
|
13
|
+
Then "option[value='cmd-joomla-project.sh']" should be attached
|
|
14
|
+
|
|
15
|
+
Scenario: The AI Agents folder lists the agents the plugin ships
|
|
16
|
+
Given I am on "/ai-agents"
|
|
17
|
+
Then I should see "joomla-site-builder"
|
|
18
|
+
And I should see "joomla-tester"
|
|
19
|
+
|
|
20
|
+
Scenario: The AI Skills folder lists the skill the plugin ships
|
|
21
|
+
Given I am on "/ai-skills"
|
|
22
|
+
Then I should see "joomla-scenarios"
|
|
23
|
+
|
|
24
|
+
Scenario: The AI Commands folder lists the command the plugin ships
|
|
25
|
+
Given I am on "/ai-commands"
|
|
26
|
+
Then I should see "joomla-test"
|
|
27
|
+
|
|
28
|
+
Scenario: The AI Prompts folder lists the prompt the plugin ships
|
|
29
|
+
Given I am on "/ai-prompts"
|
|
30
|
+
Then I should see "joomla-build-and-test"
|
package/package.json
CHANGED
|
@@ -1,6 +1,97 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webship.co/workspace-joomla",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.8",
|
|
4
|
+
"description": "Joomla for Webship Workspace: the builder (`cmd-joomla-project.sh`) and the Joomla CLI tool in every sites folder, the default accounts, the Joomla step pack wired into the testing setup, and its AI items in their folders: two agents, a skill, a command and a prompt.",
|
|
5
|
+
"main": "dist/src/index.js",
|
|
6
|
+
"types": "dist/src/index.d.ts",
|
|
7
|
+
"license": "ISC",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/webship/workspace-joomla.git"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist/src",
|
|
14
|
+
"ai",
|
|
15
|
+
"commands",
|
|
16
|
+
"docs",
|
|
17
|
+
"features",
|
|
18
|
+
"scripts",
|
|
19
|
+
"testing",
|
|
20
|
+
"README.md"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc -p tsconfig.json",
|
|
24
|
+
"prepare": "npm run build",
|
|
25
|
+
"test": "npm run build && node --test dist/tests/*.test.js",
|
|
26
|
+
"lint": "tsc -p tsconfig.json --noEmit"
|
|
27
|
+
},
|
|
28
|
+
"workspacePlugin": {
|
|
29
|
+
"name": "joomla",
|
|
30
|
+
"label": "Joomla",
|
|
31
|
+
"needs": [
|
|
32
|
+
[
|
|
33
|
+
"dev",
|
|
34
|
+
"test",
|
|
35
|
+
"sandboxes",
|
|
36
|
+
"demos"
|
|
37
|
+
],
|
|
38
|
+
"ai-agents",
|
|
39
|
+
"ai-skills",
|
|
40
|
+
"ai-commands",
|
|
41
|
+
"ai-prompts"
|
|
42
|
+
],
|
|
43
|
+
"commands": {
|
|
44
|
+
"types": [
|
|
45
|
+
"sites"
|
|
46
|
+
],
|
|
47
|
+
"workspaces": [
|
|
48
|
+
"dev",
|
|
49
|
+
"test",
|
|
50
|
+
"sandboxes",
|
|
51
|
+
"demos"
|
|
52
|
+
],
|
|
53
|
+
"files": [
|
|
54
|
+
"commands/cmd-joomla-project.sh",
|
|
55
|
+
"commands/cmd-tools-joomla-cli.sh"
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
"scripts": {
|
|
59
|
+
"functions": [
|
|
60
|
+
"scripts/functions/fun-joomla.sh"
|
|
61
|
+
],
|
|
62
|
+
"args": [
|
|
63
|
+
"scripts/args/arg-joomla.sh",
|
|
64
|
+
"scripts/args/arg-joomla-cli.sh"
|
|
65
|
+
],
|
|
66
|
+
"machine": [
|
|
67
|
+
"testing/site.feature"
|
|
68
|
+
]
|
|
69
|
+
},
|
|
70
|
+
"ai": {
|
|
71
|
+
"agents": [
|
|
72
|
+
"ai/agents/joomla-site-builder.md",
|
|
73
|
+
"ai/agents/joomla-tester.md"
|
|
74
|
+
],
|
|
75
|
+
"skills": [
|
|
76
|
+
"ai/skills/joomla-scenarios"
|
|
77
|
+
],
|
|
78
|
+
"commands": [
|
|
79
|
+
"ai/commands/joomla-test.md"
|
|
80
|
+
],
|
|
81
|
+
"prompts": [
|
|
82
|
+
"ai/prompts/joomla-build-and-test.md"
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
"features": [
|
|
86
|
+
"features/joomla.feature"
|
|
87
|
+
]
|
|
88
|
+
},
|
|
89
|
+
"devDependencies": {
|
|
90
|
+
"@types/node": "^24.0.0",
|
|
91
|
+
"@webship.co/workspace-plugin-api": "^1.0.7",
|
|
92
|
+
"typescript": "^5.9.0"
|
|
93
|
+
},
|
|
94
|
+
"publishConfig": {
|
|
95
|
+
"access": "public"
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
argparse "$@" <<ARGEOF || exit 1
|
|
4
|
+
parser.add_argument('PROJECT_NAME',
|
|
5
|
+
help='The Joomla project in this workspace.')
|
|
6
|
+
parser.add_argument('-r', '--run',
|
|
7
|
+
default="_none_",
|
|
8
|
+
help='A Joomla CLI command to run in the project, without the leading php cli/joomla.php. Example: --run "extension:list --type=plugin"')
|
|
9
|
+
parser.add_argument('-u', '--update',
|
|
10
|
+
action='store_true',
|
|
11
|
+
default=False,
|
|
12
|
+
help='Update Joomla itself to the latest release of its line, and clean the cache.')
|
|
13
|
+
parser.add_argument('-k', '--check',
|
|
14
|
+
action='store_true',
|
|
15
|
+
default=False,
|
|
16
|
+
help='Look for updates of Joomla and of its extensions, and change nothing.')
|
|
17
|
+
parser.add_argument('-a', '--add-users',
|
|
18
|
+
action='store_true',
|
|
19
|
+
default=False,
|
|
20
|
+
help='Add the default accounts, one per user group. An account that is already there is left as it is.')
|
|
21
|
+
parser.add_argument('-c', '--cancel-users',
|
|
22
|
+
action='store_true',
|
|
23
|
+
default=False,
|
|
24
|
+
help='Delete the default accounts.')
|
|
25
|
+
ARGEOF
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
argparse "$@" <<ARGEOF || exit 1
|
|
4
|
+
parser.add_argument('PROJECT_NAME',
|
|
5
|
+
help='The name of the project: its folder, its DDEV project and its host name.')
|
|
6
|
+
parser.add_argument('-v', '--version',
|
|
7
|
+
default="${site_version}",
|
|
8
|
+
help='The Joomla release to download: latest, or a version number. Example: --version 6.2.0 [default %(default)s]')
|
|
9
|
+
parser.add_argument('-i', '--install',
|
|
10
|
+
action='store_true',
|
|
11
|
+
default=False,
|
|
12
|
+
help='Install Joomla, with the workspace account as the Super User.')
|
|
13
|
+
parser.add_argument('-a', '--add-users',
|
|
14
|
+
action='store_true',
|
|
15
|
+
default=False,
|
|
16
|
+
help='With --install, add the default accounts: one per user group, the ones the test scenarios log in as.')
|
|
17
|
+
parser.add_argument('-s', '--sample-data',
|
|
18
|
+
action='store_true',
|
|
19
|
+
default=False,
|
|
20
|
+
help='With --install, add the blog sample data of Joomla: articles, a menu and modules.')
|
|
21
|
+
parser.add_argument('-l', '--launch',
|
|
22
|
+
action='store_true',
|
|
23
|
+
default=False,
|
|
24
|
+
help='Open the site in a browser when it is ready.')
|
|
25
|
+
parser.add_argument('--php-version',
|
|
26
|
+
default="8.3",
|
|
27
|
+
choices=['8.5', '8.4', '8.3'],
|
|
28
|
+
help='PHP version for the project container. Example: --php-version 8.4 [default %(default)s]')
|
|
29
|
+
ARGEOF
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# Joomla for the Webship Workspace: build a site, run the Joomla CLI on it, add its default
|
|
4
|
+
# accounts, and wire its step pack into the testing setup.
|
|
5
|
+
#
|
|
6
|
+
# Placed by `plugins.sh --install joomla` in core/scripts/plugins/joomla/functions/, where
|
|
7
|
+
# bootstrap sources it after the core's own functions. Everything runs in the project's container
|
|
8
|
+
# (DDEV by default: `ddev exec php <docroot>/cli/joomla.php …`), never on the host.
|
|
9
|
+
#
|
|
10
|
+
# The container tool has no project type for Joomla, so a Joomla project is a `php` project that
|
|
11
|
+
# carries the Joomla CLI in its docroot; that file is how the functions here recognise one. The
|
|
12
|
+
# installer is given the container's own database values (db / db / db) and nothing personal.
|
|
13
|
+
#
|
|
14
|
+
# Four names are the ones the core's tools look for:
|
|
15
|
+
# set_joomla_users the default accounts (cmd-tools-add-users.sh <project> joomla)
|
|
16
|
+
# add_joomla_users create them with the Joomla CLI (the same tool, and --add-users of the builder)
|
|
17
|
+
# cancel_joomla_users delete them (cmd-tools-cancel-users.sh <project> joomla)
|
|
18
|
+
# testing_configure_joomla the step pack in the testing stack (cmd-tools-testing.sh <project>)
|
|
19
|
+
|
|
20
|
+
# The step pack a Joomla project is tested with, and the release line it is required at.
|
|
21
|
+
readonly JOOMLA_STEPS_PACKAGE='@webship-js/joomla-steps';
|
|
22
|
+
readonly JOOMLA_STEPS_VERSION='^2.1.0';
|
|
23
|
+
|
|
24
|
+
# The release a build falls back on when the latest one cannot be read: the one this plugin was proven on.
|
|
25
|
+
readonly JOOMLA_PROVEN_VERSION='6.2.0';
|
|
26
|
+
|
|
27
|
+
# Joomla refuses a password shorter than this.
|
|
28
|
+
readonly JOOMLA_PASSWORD_LENGTH=12;
|
|
29
|
+
|
|
30
|
+
# The default accounts: one per user group a scenario logs in as. Accounts are configuration,
|
|
31
|
+
# created once on the site; a test suite never creates or deletes one. The role is the Joomla
|
|
32
|
+
# user group, by its title.
|
|
33
|
+
function set_joomla_users() {
|
|
34
|
+
users=(administrator manager editor member);
|
|
35
|
+
local groups=('Super Users' 'Manager' 'Editor' 'Registered') i user ;
|
|
36
|
+
for i in "${!users[@]}" ; do
|
|
37
|
+
user="${users[$i]}" ;
|
|
38
|
+
printf -v "user_${user}_name" '%s' "${user}" ;
|
|
39
|
+
printf -v "user_${user}_mail" '%s' "${user}@example.com" ;
|
|
40
|
+
printf -v "user_${user}_password" '%s' 'dD.123123ddd' ;
|
|
41
|
+
printf -v "user_${user}_role" '%s' "${groups[$i]}" ;
|
|
42
|
+
done
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# The docroot of a project, from its own container configuration.
|
|
46
|
+
function joomla_docroot() {
|
|
47
|
+
local project_path="$1" docroot ;
|
|
48
|
+
docroot="$(grep -E '^docroot:' "${project_path}/.ddev/config.yaml" 2>/dev/null | head -1 | sed -E 's/^docroot:[[:space:]]*//; s/^"(.*)"$/\1/; s/[[:space:]]*$//')" ;
|
|
49
|
+
printf '%s' "${docroot}" ;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# Where the Joomla CLI is, from the project root: <docroot>/cli/joomla.php.
|
|
53
|
+
function joomla_cli_path() {
|
|
54
|
+
local docroot ; docroot="$(joomla_docroot "$1")" ;
|
|
55
|
+
printf '%s' "${docroot:+${docroot}/}cli/joomla.php" ;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# Is this folder a Joomla project? The Joomla CLI and the administrator in its docroot say so.
|
|
59
|
+
function joomla_is_project() {
|
|
60
|
+
local project_path="$1" docroot ;
|
|
61
|
+
[ -f "${project_path}/.ddev/config.yaml" ] || return 1 ;
|
|
62
|
+
docroot="$(joomla_docroot "${project_path}")" ;
|
|
63
|
+
[ -f "${project_path}/${docroot:+${docroot}/}cli/joomla.php" ] && [ -d "${project_path}/${docroot:+${docroot}/}administrator" ] ;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# Is the site installed? The installer writes configuration.php, and nothing else does.
|
|
67
|
+
function joomla_is_installed() {
|
|
68
|
+
local project_path="$1" docroot ;
|
|
69
|
+
docroot="$(joomla_docroot "${project_path}")" ;
|
|
70
|
+
[ -f "${project_path}/${docroot:+${docroot}/}configuration.php" ] ;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# Go to the project a tool was asked about, or say why not.
|
|
74
|
+
function joomla_enter_project() {
|
|
75
|
+
local project_path="${WORKSPACE_ROOT}/${doc_name}/${PROJECT_NAME}";
|
|
76
|
+
if [ ! -d "${project_path}" ] ; then
|
|
77
|
+
echo "No project ${PROJECT_NAME} in ${WORKSPACE_ROOT}/${doc_name}." ;
|
|
78
|
+
return 1 ;
|
|
79
|
+
fi
|
|
80
|
+
if ! joomla_is_project "${project_path}" ; then
|
|
81
|
+
echo "${doc_name}/${PROJECT_NAME} is not a Joomla project (its docroot has no cli/joomla.php)." ;
|
|
82
|
+
return 1 ;
|
|
83
|
+
fi
|
|
84
|
+
cd "${project_path}" || return 1 ;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# Run the Joomla CLI in the container of the project the shell is in. Every argument is quoted
|
|
88
|
+
# for the container's shell, so a name with a space ("Super Users") arrives as one argument.
|
|
89
|
+
function joomla_cli() {
|
|
90
|
+
local line arg ;
|
|
91
|
+
line="php $(joomla_cli_path "$(pwd)")" ;
|
|
92
|
+
for arg in "$@" ; do
|
|
93
|
+
line="${line} $(printf '%q' "${arg}")" ;
|
|
94
|
+
done
|
|
95
|
+
ddev exec "${line}" ;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
# Is there an account of this username on the site?
|
|
99
|
+
function joomla_user_exists() {
|
|
100
|
+
# A row of the list: the number of the account, then its username.
|
|
101
|
+
joomla_cli user:list --no-interaction 2>/dev/null | grep -qE "^[[:space:]]*[0-9]+[[:space:]]+$1[[:space:]]" ;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
# The number of the latest stable release, read in the container from where the releases are
|
|
105
|
+
# published; the proven release when it cannot be read.
|
|
106
|
+
function joomla_latest_version() {
|
|
107
|
+
local version ;
|
|
108
|
+
version="$(ddev exec "curl -fsSI https://github.com/joomla/joomla-cms/releases/latest" 2>/dev/null \
|
|
109
|
+
| tr -d '\r' | sed -nE 's#^[Ll]ocation:.*/tag/([0-9][0-9A-Za-z.-]*)$#\1#p' | head -1)" ;
|
|
110
|
+
printf '%s' "${version:-${JOOMLA_PROVEN_VERSION}}" ;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
# Build a Joomla site from nothing: the container, the code, and with --install the site itself.
|
|
114
|
+
function joomla_build() {
|
|
115
|
+
trim_project_name ;
|
|
116
|
+
local base_url="https://${PROJECT_NAME}.ddev.site" ;
|
|
117
|
+
local version="${VERSION:-${site_version:-latest}}" ;
|
|
118
|
+
local php_version="${PHP_VERSION:-${site_php_version:-8.3}}" ;
|
|
119
|
+
local docroot="${distribution_webroot}" ;
|
|
120
|
+
local web="${docroot:+${docroot}/}" ;
|
|
121
|
+
|
|
122
|
+
if [ "${INSTALL}" == 'yes' ] && [ "${#account_pass}" -lt "${JOOMLA_PASSWORD_LENGTH}" ] ; then
|
|
123
|
+
echo "Joomla asks for a password of ${JOOMLA_PASSWORD_LENGTH} characters or more, and the workspace account's is shorter." ;
|
|
124
|
+
echo "Change account: pass in core/config/settings.yml, or build without --install." ;
|
|
125
|
+
exit 1 ;
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
cd ${WORKSPACE_ROOT}/${doc_name} || exit 1 ;
|
|
129
|
+
|
|
130
|
+
drop_database ;
|
|
131
|
+
rm -rf "${PROJECT_NAME}" ;
|
|
132
|
+
mkdir -p "${PROJECT_NAME}/${docroot}" ;
|
|
133
|
+
cd ${WORKSPACE_ROOT}/${doc_name}/${PROJECT_NAME} || exit 1 ;
|
|
134
|
+
|
|
135
|
+
ddev config --project-type=php --docroot="${docroot}" --project-name=${PROJECT_NAME} \
|
|
136
|
+
--php-version=${php_version} --webserver-type=nginx-fpm --nodejs-version=$(ddev_nodejs_version) || exit 1 ;
|
|
137
|
+
ddev_up start -y || exit 1 ;
|
|
138
|
+
|
|
139
|
+
[ "${version}" == 'latest' ] && version="$(joomla_latest_version)" ;
|
|
140
|
+
local package="https://github.com/joomla/joomla-cms/releases/download/${version}/Joomla_${version}-Stable-Full_Package.zip" ;
|
|
141
|
+
echo "Downloading Joomla ${version}." ;
|
|
142
|
+
ddev exec "curl -fsSL -o /tmp/joomla.zip '${package}' && unzip -q -o /tmp/joomla.zip -d '/var/www/html/${docroot}' && rm -f /tmp/joomla.zip" \
|
|
143
|
+
|| { echo "The Joomla ${version} package could not be downloaded or unpacked." ; exit 1 ; } ;
|
|
144
|
+
|
|
145
|
+
if [ "${INSTALL}" == 'yes' ] ; then
|
|
146
|
+
# The container's own database values, the same in every project: nothing personal is written.
|
|
147
|
+
ddev exec "php ${web}installation/joomla.php install --no-interaction \
|
|
148
|
+
--site-name=$(printf '%q' "${doc_name} ${PROJECT_NAME}") \
|
|
149
|
+
--admin-user=$(printf '%q' "${account_name}") --admin-username=$(printf '%q' "${account_name}") \
|
|
150
|
+
--admin-password=$(printf '%q' "${account_pass}") --admin-email=$(printf '%q' "${account_mail}") \
|
|
151
|
+
--db-type=mysqli --db-host=db --db-user=db --db-pass=db --db-name=db --db-prefix=jos_" \
|
|
152
|
+
|| { echo "The Joomla installer failed." ; exit 1 ; } ;
|
|
153
|
+
|
|
154
|
+
# The administrator asks every new site to share anonymous statistics, with a notice on each
|
|
155
|
+
# page. A local site answers "never" once.
|
|
156
|
+
ddev mysql -e "UPDATE jos_extensions SET params='{\"mode\":3}' WHERE element='stats' AND folder='system'" ;
|
|
157
|
+
|
|
158
|
+
if [ "${SAMPLE_DATA}" == 'yes' ] ; then
|
|
159
|
+
joomla_sample_data ;
|
|
160
|
+
fi
|
|
161
|
+
|
|
162
|
+
if [ "${ADD_USERS}" == 'yes' ] ; then
|
|
163
|
+
set_joomla_users ;
|
|
164
|
+
add_joomla_users ;
|
|
165
|
+
cd ${WORKSPACE_ROOT}/${doc_name}/${PROJECT_NAME} || exit 1 ;
|
|
166
|
+
fi
|
|
167
|
+
|
|
168
|
+
echo "${doc_name} ${PROJECT_NAME} has been installed: Joomla ${version}.";
|
|
169
|
+
echo "Go to ${base_url}/administrator and log in as ${account_name}";
|
|
170
|
+
else
|
|
171
|
+
[ "${ADD_USERS}" == 'yes' ] && echo "--add-users needs --install: there is no site to add accounts to yet." ;
|
|
172
|
+
[ "${SAMPLE_DATA}" == 'yes' ] && echo "--sample-data needs --install: there is no site to add content to yet." ;
|
|
173
|
+
echo "${doc_name} ${PROJECT_NAME} is ready to install.";
|
|
174
|
+
echo "Go to ${base_url}";
|
|
175
|
+
fi
|
|
176
|
+
|
|
177
|
+
if [ "${LAUNCH}" == 'yes' ] ; then
|
|
178
|
+
ddev launch ;
|
|
179
|
+
fi
|
|
180
|
+
|
|
181
|
+
cd ${WORKSPACE_ROOT}/${doc_name};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
# The "blog" sample data of Joomla: articles, a menu and the modules that show them. Joomla
|
|
185
|
+
# offers it through the administrator only, so the container logs in as the workspace account,
|
|
186
|
+
# the way a browser would, and asks for each of its four steps in order; then the Smart Search
|
|
187
|
+
# index is built, which the sample data leaves empty. The password travels in the container's
|
|
188
|
+
# environment for that one command and is written nowhere.
|
|
189
|
+
function joomla_sample_data() {
|
|
190
|
+
echo "Adding the blog sample data." ;
|
|
191
|
+
ddev exec "JOOMLA_USER=$(printf '%q' "${account_name}") JOOMLA_PASSWORD=$(printf '%q' "${account_pass}") bash -s" <<'SAMPLEEOF' || { echo " The sample data could not be added; the site itself is installed." ; return 1 ; } ;
|
|
192
|
+
set -e
|
|
193
|
+
site="${DDEV_PRIMARY_URL%/}"
|
|
194
|
+
jar="$(mktemp)"
|
|
195
|
+
trap 'rm -f "${jar}"' EXIT
|
|
196
|
+
admin="${site}/administrator/index.php"
|
|
197
|
+
login_token="$(curl -ksL -c "${jar}" "${admin}" | grep -oP '(?<=name=")[a-f0-9]{32}(?=" value="1")' | head -1)"
|
|
198
|
+
curl -ksL -o /dev/null -b "${jar}" -c "${jar}" "${admin}" \
|
|
199
|
+
--data-urlencode "username=${JOOMLA_USER}" --data-urlencode "passwd=${JOOMLA_PASSWORD}" \
|
|
200
|
+
--data-urlencode "option=com_login" --data-urlencode "task=login" \
|
|
201
|
+
--data-urlencode "return=aW5kZXgucGhw" --data-urlencode "${login_token}=1"
|
|
202
|
+
# A token of the session that is now open: the one of the login form is stale by now.
|
|
203
|
+
token="$(curl -ksL -b "${jar}" -c "${jar}" "${admin}" | grep -oP '(?<=csrf.token":")[a-f0-9]{32}' | head -1)"
|
|
204
|
+
[ -n "${token}" ] || { echo " The administrator did not open a session." ; exit 1 ; }
|
|
205
|
+
# In order: the second step makes the menu the third one attaches the modules to.
|
|
206
|
+
for step in 1 2 3 4 ; do
|
|
207
|
+
curl -ksL -b "${jar}" -c "${jar}" -o /dev/null \
|
|
208
|
+
"${admin}?option=com_ajax&group=sampledata&type=blog&plugin=SampledataApplyStep${step}&format=json&${token}=1"
|
|
209
|
+
done
|
|
210
|
+
SAMPLEEOF
|
|
211
|
+
joomla_cli finder:index --no-interaction ;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
# Create the default accounts with the Joomla CLI. An account that is already there is left as it is.
|
|
215
|
+
# The list is loaded by the caller (set_joomla_users): the core's add_users, the builder, the CLI tool.
|
|
216
|
+
function add_joomla_users() {
|
|
217
|
+
joomla_enter_project || return 1 ;
|
|
218
|
+
local user user_name user_mail user_password user_role ;
|
|
219
|
+
for user in "${users[@]}" ; do
|
|
220
|
+
user_name="user_${user}_name"; user_mail="user_${user}_mail";
|
|
221
|
+
user_password="user_${user}_password"; user_role="user_${user}_role";
|
|
222
|
+
|
|
223
|
+
echo " ---------------------------------------------------------------- ";
|
|
224
|
+
echo " User name: ${!user_name}";
|
|
225
|
+
echo " User mail: ${!user_mail}";
|
|
226
|
+
echo " User password: ${!user_password}";
|
|
227
|
+
echo " User role: ${!user_role}";
|
|
228
|
+
echo " ================================================================= ";
|
|
229
|
+
|
|
230
|
+
if joomla_user_exists "${!user_name}" ; then
|
|
231
|
+
echo " ${!user_name} is already there." ;
|
|
232
|
+
else
|
|
233
|
+
joomla_cli user:add --no-interaction --username="${!user_name}" --name="${!user_name^}" \
|
|
234
|
+
--password="${!user_password}" --email="${!user_mail}" --usergroup="${!user_role}" ;
|
|
235
|
+
fi
|
|
236
|
+
done
|
|
237
|
+
cd ${WORKSPACE_ROOT}/${doc_name};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
# Delete the default accounts.
|
|
241
|
+
function cancel_joomla_users() {
|
|
242
|
+
joomla_enter_project || return 1 ;
|
|
243
|
+
local user user_name ;
|
|
244
|
+
for user in "${users[@]}" ; do
|
|
245
|
+
user_name="user_${user}_name";
|
|
246
|
+
echo " ---------------------------------------------------------------- ";
|
|
247
|
+
echo " User name: ${!user_name}";
|
|
248
|
+
echo " ================================================================= ";
|
|
249
|
+
if joomla_user_exists "${!user_name}" ; then
|
|
250
|
+
joomla_cli user:delete --no-interaction --username="${!user_name}" ;
|
|
251
|
+
else
|
|
252
|
+
echo " ${!user_name} is not there." ;
|
|
253
|
+
fi
|
|
254
|
+
done
|
|
255
|
+
cd ${WORKSPACE_ROOT}/${doc_name};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
# What cmd-tools-joomla-cli.sh does: the first of --run, --update, --check, --add-users,
|
|
259
|
+
# --cancel-users that was asked for; with none of them, what the site is.
|
|
260
|
+
function joomla_cli_tool() {
|
|
261
|
+
trim_project_name ;
|
|
262
|
+
joomla_enter_project || exit 1 ;
|
|
263
|
+
|
|
264
|
+
if ! ddev describe -j 2>/dev/null | grep -q '"status":"running"' ; then
|
|
265
|
+
ddev start -y || exit 1 ;
|
|
266
|
+
fi
|
|
267
|
+
|
|
268
|
+
if [ ! "${RUN}" == '_none_' ] ; then
|
|
269
|
+
# One string on purpose: --run "extension:list --type=plugin" is a command and its arguments.
|
|
270
|
+
ddev exec "php $(joomla_cli_path "$(pwd)") ${RUN}" ;
|
|
271
|
+
exit $? ;
|
|
272
|
+
fi
|
|
273
|
+
|
|
274
|
+
if [ "${UPDATE}" == 'yes' ] ; then
|
|
275
|
+
joomla_cli core:update --no-interaction && joomla_cli cache:clean --no-interaction ;
|
|
276
|
+
elif [ "${CHECK}" == 'yes' ] ; then
|
|
277
|
+
joomla_cli core:check-updates --no-interaction ;
|
|
278
|
+
joomla_cli update:extensions:check --no-interaction ;
|
|
279
|
+
elif [ "${ADD_USERS}" == 'yes' ] ; then
|
|
280
|
+
set_joomla_users ;
|
|
281
|
+
add_joomla_users ;
|
|
282
|
+
elif [ "${CANCEL_USERS}" == 'yes' ] ; then
|
|
283
|
+
set_joomla_users ;
|
|
284
|
+
cancel_joomla_users ;
|
|
285
|
+
else
|
|
286
|
+
echo "${doc_name}/${PROJECT_NAME}: $(joomla_cli --version 2>/dev/null | head -1) at https://${PROJECT_NAME}.ddev.site" ;
|
|
287
|
+
if joomla_is_installed "$(pwd)" ; then
|
|
288
|
+
echo "" ; echo "Accounts:" ; joomla_cli user:list --no-interaction ;
|
|
289
|
+
else
|
|
290
|
+
echo "The site is not installed yet: open its address, or build it again with --install." ;
|
|
291
|
+
fi
|
|
292
|
+
fi
|
|
293
|
+
cd ${WORKSPACE_ROOT}/${doc_name};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
# The step pack in the testing stack of a Joomla project. The core's testing_configure calls
|
|
297
|
+
# this in the project folder once webship-js is scaffolded and before the dependencies are
|
|
298
|
+
# installed; for a project that is not Joomla it changes nothing.
|
|
299
|
+
#
|
|
300
|
+
# It adds the pack to package.json, lists its step definitions in cucumber.js after webship-js's
|
|
301
|
+
# own, gives cucumber.js the users registry of the default accounts (a handle a scenario reads,
|
|
302
|
+
# mapped to an account; never the workspace account), and writes a first feature when the project
|
|
303
|
+
# has none of its own. Every part is skipped when it is already there, so a second run adds what
|
|
304
|
+
# the first missed and never resets a suite.
|
|
305
|
+
function testing_configure_joomla() {
|
|
306
|
+
local project_path="$1";
|
|
307
|
+
joomla_is_project "${project_path}" || return 0 ;
|
|
308
|
+
if [ ! -f "${project_path}/cucumber.js" ] || [ ! -f "${project_path}/package.json" ] ; then
|
|
309
|
+
echo " Joomla: cucumber.js is not here yet — run the testing setup again once webship-js is scaffolded." ;
|
|
310
|
+
return 1 ;
|
|
311
|
+
fi
|
|
312
|
+
|
|
313
|
+
echo "Adding the Joomla step pack (${JOOMLA_STEPS_PACKAGE} ${JOOMLA_STEPS_VERSION}).";
|
|
314
|
+
set_joomla_users ;
|
|
315
|
+
local registry="" user user_name user_password handle ;
|
|
316
|
+
for user in "${users[@]}" ; do
|
|
317
|
+
user_name="user_${user}_name"; user_password="user_${user}_password";
|
|
318
|
+
# The pack's features call the Super User "admin".
|
|
319
|
+
handle="${user}" ; [ "${user}" == 'administrator' ] && handle="admin" ;
|
|
320
|
+
registry="${registry}${handle}|${!user_name}|${!user_password}"$'\n' ;
|
|
321
|
+
done
|
|
322
|
+
|
|
323
|
+
python3 - "${project_path}" "${JOOMLA_STEPS_PACKAGE}" "${JOOMLA_STEPS_VERSION}" 'JOOMLA' "${registry}" <<'PYEOF' || return 1
|
|
324
|
+
import json, re, sys
|
|
325
|
+
|
|
326
|
+
project, pack, version, prefix, registry = sys.argv[1:6]
|
|
327
|
+
|
|
328
|
+
# package.json: the pack as a dev dependency, at the release line this plugin was proven on.
|
|
329
|
+
pkg_file = project + '/package.json'
|
|
330
|
+
pkg = json.load(open(pkg_file))
|
|
331
|
+
if pack not in pkg.get('dependencies', {}) and pack not in pkg.get('devDependencies', {}):
|
|
332
|
+
pkg.setdefault('devDependencies', {})[pack] = version
|
|
333
|
+
open(pkg_file, 'w').write(json.dumps(pkg, indent=2) + '\n')
|
|
334
|
+
print(' package.json: %s %s' % (pack, version))
|
|
335
|
+
|
|
336
|
+
# cucumber.js: the pack's step definitions after webship-js's own, and the users registry.
|
|
337
|
+
cucumber_file = project + '/cucumber.js'
|
|
338
|
+
src = open(cucumber_file).read()
|
|
339
|
+
before = src
|
|
340
|
+
|
|
341
|
+
if ('node_modules/%s/' % pack) not in src:
|
|
342
|
+
line = "'node_modules/%s/tests/step-definitions/**/*.js'," % pack
|
|
343
|
+
core = re.search(r"^([ \t]*)'node_modules/webship-js/tests/step-definitions/[^']*',.*$", src, re.M)
|
|
344
|
+
opening = re.search(r"^([ \t]*)require:[ \t]*\[[ \t]*$", src, re.M)
|
|
345
|
+
if core:
|
|
346
|
+
src = src[:core.end()] + '\n' + core.group(1) + line + src[core.end():]
|
|
347
|
+
elif opening:
|
|
348
|
+
src = src[:opening.end()] + '\n' + opening.group(1) + ' ' + line + src[opening.end():]
|
|
349
|
+
else:
|
|
350
|
+
sys.exit(" cucumber.js has no require list this can add to. Add this line to it:\n " + line)
|
|
351
|
+
print(' cucumber.js: the step definitions of %s' % pack)
|
|
352
|
+
|
|
353
|
+
if not re.search(r"^[ \t]*users:[ \t]*\{", src, re.M):
|
|
354
|
+
world = re.search(r"^([ \t]*)worldParameters:[ \t]*\{[ \t]*$", src, re.M)
|
|
355
|
+
if not world:
|
|
356
|
+
sys.exit(" cucumber.js has no worldParameters block to add the users registry to.")
|
|
357
|
+
pad = world.group(1) + ' '
|
|
358
|
+
rows = [r.split('|') for r in registry.splitlines() if r.strip()]
|
|
359
|
+
block = [
|
|
360
|
+
pad + "// The users registry: a handle a scenario reads, mapped to an account of this site (the default",
|
|
361
|
+
pad + "// accounts: cmd-tools-add-users.sh). Override one with %s_<HANDLE>_USER and %s_<HANDLE>_PASSWORD." % (prefix, prefix),
|
|
362
|
+
pad + "users: {",
|
|
363
|
+
]
|
|
364
|
+
for handle, name, password in rows:
|
|
365
|
+
env = '%s_%s' % (prefix, handle.upper())
|
|
366
|
+
block.append(pad + " %s: { username: process.env.%s_USER || '%s', password: process.env.%s_PASSWORD || '%s' }," % (handle, env, name, env, password))
|
|
367
|
+
block.append(pad + "},")
|
|
368
|
+
src = src[:world.end()] + '\n' + '\n'.join(block) + src[world.end():]
|
|
369
|
+
print(' cucumber.js: the users registry (%s)' % ', '.join(r[0] for r in rows))
|
|
370
|
+
|
|
371
|
+
if src != before:
|
|
372
|
+
open(cucumber_file, 'w').write(src)
|
|
373
|
+
PYEOF
|
|
374
|
+
|
|
375
|
+
# A first feature, when the project has none for the site yet: the front page, the login page,
|
|
376
|
+
# each default account in its group.
|
|
377
|
+
local feature="${WORKSPACE_SCRIPTS}/plugins/joomla/site.feature" ;
|
|
378
|
+
if [ -f "${feature}" ] && [ ! -f "${project_path}/tests/features/site.feature" ] ; then
|
|
379
|
+
mkdir -p "${project_path}/tests/features" && cp "${feature}" "${project_path}/tests/features/site.feature" \
|
|
380
|
+
&& echo " tests/features/site.feature: a first feature for the site" ;
|
|
381
|
+
fi
|
|
382
|
+
|
|
383
|
+
# The scenarios log in as the default accounts: make sure an installed site has them.
|
|
384
|
+
if joomla_is_installed "${project_path}" ; then
|
|
385
|
+
( add_joomla_users > /dev/null ) && echo " the default accounts are on the site (${users[*]})" ;
|
|
386
|
+
fi
|
|
387
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
Feature: The site answers and its accounts work
|
|
2
|
+
As the owner of the site
|
|
3
|
+
I want the front page, the login page and each default account to work
|
|
4
|
+
So that a build that came out broken is caught before anything else is tested
|
|
5
|
+
|
|
6
|
+
# Placed by the workspace's testing setup as a starting point: it is yours to change.
|
|
7
|
+
# The accounts are the default ones (cmd-tools-add-users.sh); a scenario logs in by a handle of
|
|
8
|
+
# the users registry in cucumber.js, so no password is written here.
|
|
9
|
+
|
|
10
|
+
@critical
|
|
11
|
+
Scenario: The front page is served
|
|
12
|
+
Given I am an anonymous user
|
|
13
|
+
And I am on the homepage
|
|
14
|
+
Then the response status code should be 200
|
|
15
|
+
And the page should have a title
|
|
16
|
+
|
|
17
|
+
@critical
|
|
18
|
+
Scenario: The login page is served with its form
|
|
19
|
+
Given I am on the login page
|
|
20
|
+
Then the response status code should be 200
|
|
21
|
+
And I should see the login form
|
|
22
|
+
|
|
23
|
+
Scenario Outline: Each default account is on the site, in the group of its role
|
|
24
|
+
Given I am a logged in user with the "admin" user
|
|
25
|
+
Then the user "<account>" should exist
|
|
26
|
+
And the user "<account>" should be enabled
|
|
27
|
+
And the user "<account>" should be in the "<group>" group
|
|
28
|
+
|
|
29
|
+
Examples:
|
|
30
|
+
| account | group |
|
|
31
|
+
| administrator | Super Users |
|
|
32
|
+
| manager | Manager |
|
|
33
|
+
| editor | Editor |
|
|
34
|
+
| member | Registered |
|
|
35
|
+
|
|
36
|
+
Scenario Outline: An account of the administrator logs in to it
|
|
37
|
+
Given I am a logged in user with the "<handle>" user
|
|
38
|
+
Then I should see the administrator dashboard
|
|
39
|
+
And I should see the administrator menu
|
|
40
|
+
|
|
41
|
+
Examples:
|
|
42
|
+
| handle |
|
|
43
|
+
| admin |
|
|
44
|
+
| manager |
|
|
45
|
+
|
|
46
|
+
Scenario Outline: An account of the site logs in to it
|
|
47
|
+
Given I am logged in to the site with the "<handle>" user
|
|
48
|
+
Then I should be logged in to the site as "<handle>"
|
|
49
|
+
|
|
50
|
+
Examples:
|
|
51
|
+
| handle |
|
|
52
|
+
| editor |
|
|
53
|
+
| member |
|
|
54
|
+
|
|
55
|
+
Scenario: An account is refused a page its group may not open
|
|
56
|
+
Given I am a logged in user with the "manager" user
|
|
57
|
+
When I go to the administrator "Users" page
|
|
58
|
+
Then the response status code should be 403
|
|
59
|
+
|
|
60
|
+
Scenario: An anonymous visitor has no session
|
|
61
|
+
Given I am not logged in
|
|
62
|
+
When I am on the profile page
|
|
63
|
+
Then I should see the login form
|
|
64
|
+
And I should not be logged in to the site
|