Skip to content

Commit 2a51622

Browse files
Merge branch 'develop' into bugfix/elementor-bg-images
2 parents 70adce8 + 6d68969 commit 2a51622

35 files changed

Lines changed: 1288 additions & 96 deletions

‎.env.example‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copy to `.env` and fill in real values. `.env` is gitignored.
2+
#
3+
# Used by Playwright (tests/e2e/playwright.config.js) at e2e startup.
4+
5+
# Cloudinary connection string consumed by the wizard e2e spec
6+
# (tests/e2e/wizard-setup.spec.js). Use a dedicated test account —
7+
# never production credentials. See README "End-to-end testing" for
8+
# why this is named CLOUDINARY_E2E_URL rather than CLOUDINARY_URL.
9+
CLOUDINARY_E2E_URL=cloudinary://API_KEY:API_SECRET@CLOUD_NAME

‎.github/workflows/ci.yml‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ name: CI
22

33
on:
44
push:
5+
pull_request:
56

67
jobs:
78
build:
@@ -41,3 +42,45 @@ jobs:
4142

4243
- name: Run build
4344
run: npm run build
45+
46+
e2e:
47+
name: E2E (Playwright)
48+
runs-on: ubuntu-latest
49+
steps:
50+
- name: Checkout repository
51+
uses: actions/checkout@v4
52+
53+
- name: Setup Node.js
54+
uses: actions/setup-node@v4
55+
with:
56+
node-version: '22'
57+
cache: 'npm'
58+
59+
- name: Install dependencies
60+
run: npm ci
61+
62+
- name: Install Playwright browsers
63+
run: npx playwright install --with-deps chromium
64+
65+
- name: Build assets
66+
run: npm run build
67+
68+
- name: Start wp-env
69+
run: npm run env:start
70+
71+
- name: Run E2E tests
72+
env:
73+
CLOUDINARY_E2E_URL: ${{ secrets.CLOUDINARY_E2E_URL }}
74+
run: npm run test:e2e
75+
76+
- name: Stop wp-env
77+
if: always()
78+
run: npm run env:stop
79+
80+
- name: Upload Playwright artifacts
81+
if: failure()
82+
uses: actions/upload-artifact@v4
83+
with:
84+
name: playwright-artifacts
85+
path: artifacts/
86+
retention-days: 7

‎.gitignore‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ js/*.map
3636
tests/data/
3737
tests/includes/
3838
coverage/html/
39+
/artifacts/
40+
/playwright-report/
41+
/test-results/
3942

4043
# ENV files
4144
.env

‎README.md‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,59 @@ Files included in the release package are defined in the `gruntfile.js` under th
117117

118118
3. Run `npm run deploy-assets` to deploy just the WP.org plugin assets such as screenshots, icons and banners.
119119

120+
## End-to-end testing
121+
122+
E2E tests run against a wp-env site using Playwright.
123+
124+
### One-time setup
125+
126+
```bash
127+
npm install
128+
npx playwright install --with-deps chromium
129+
npm run env:start
130+
```
131+
132+
### Running the tests
133+
134+
```bash
135+
npm run test:e2e
136+
```
137+
138+
### Wizard test credentials
139+
140+
`tests/e2e/wizard-setup.spec.js` exercises the live Cloudinary connection flow, so it needs a real connection string. Provide one of two ways:
141+
142+
**Option 1 — `.env` file (recommended for sustained local development).** Copy `.env.example` to `.env` and fill in the value. `.env` is gitignored. Playwright loads it automatically at startup.
143+
144+
```bash
145+
cp .env.example .env
146+
# edit .env, set CLOUDINARY_E2E_URL=cloudinary://...
147+
npm run test:e2e
148+
```
149+
150+
**Option 2 — shell export (good for one-off runs and CI).**
151+
152+
```bash
153+
export CLOUDINARY_E2E_URL='cloudinary://API_KEY:API_SECRET@CLOUD_NAME'
154+
npm run test:e2e
155+
```
156+
157+
A real shell env var takes precedence over the `.env` file.
158+
159+
The variable is intentionally named `CLOUDINARY_E2E_URL` (not `CLOUDINARY_URL`) so it cannot be confused with the Cloudinary SDK convention or with anything you might define in `.wp-env.override.json` for local dev. Use a dedicated test Cloudinary account — never production credentials.
160+
161+
> **Note:** Do **not** set `CLOUDINARY_URL` or `CLOUDINARY_CONNECTION_STRING` as PHP constants via `.wp-env.override.json` while running this spec. The plugin treats a constant-defined connection string as already-configured and hides the wizard's connection input, which makes the test impossible.
162+
163+
CI will provide `CLOUDINARY_E2E_URL` via a GitHub Actions secret (wired separately under WPP-1195's CI subtask).
164+
165+
### Debugging a failing e2e test
166+
167+
```bash
168+
npm run test:e2e:debug -- wizard-setup
169+
```
170+
171+
This opens Playwright's UI runner where you can step through actions, inspect the DOM, and view the network panel.
172+
120173
## License
121174
122175
Released under the GPL license.

‎css/cloudinary.css‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎js/cloudinary.js‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package-lock.json‎

Lines changed: 3 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@
3838
"release": "release-it --no-increment",
3939
"release:ci": "release-it --ci",
4040
"release:dry": "release-it --dry-run",
41-
"prepare": "husky"
41+
"prepare": "husky",
42+
"test:e2e": "playwright test --config tests/e2e/playwright.config.js",
43+
"test:e2e:debug": "playwright test --config tests/e2e/playwright.config.js --ui"
4244
},
4345
"lint-staged": {
4446
"*.php": [
@@ -67,6 +69,7 @@
6769
"tippy.js": "^6.3.1"
6870
},
6971
"devDependencies": {
72+
"@playwright/test": "^1.59.1",
7073
"@release-it/bumper": "^7.0.5",
7174
"@typescript-eslint/eslint-plugin": "^8.46.3",
7275
"@wordpress/api-fetch": "^7.34.0",
@@ -75,6 +78,7 @@
7578
"@wordpress/browserslist-config": "^6.34.0",
7679
"@wordpress/components": "^30.7.0",
7780
"@wordpress/data": "^10.34.0",
81+
"@wordpress/e2e-test-utils-playwright": "^1.44.0",
7882
"@wordpress/element": "^6.34.0",
7983
"@wordpress/env": "^10.12.0",
8084
"@wordpress/eslint-plugin": "^22.20.0",
@@ -85,6 +89,7 @@
8589
"css-minimizer-webpack-plugin": "^7.0.2",
8690
"css-unicode-loader": "^1.0.3",
8791
"cssnano": "^7.1.2",
92+
"dotenv": "^17.3.1",
8893
"eslint": "^8.57.1",
8994
"eslint-plugin-jest": "^29.0.1",
9095
"eslint-plugin-react-hooks": "^7.0.1",

‎php/class-connect.php‎

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,13 @@ function ( $a ) {
447447
public function history( $days = 1 ) {
448448
$return = array();
449449
$history = get_option( self::META_KEYS['history'], array() );
450-
$plan = ! empty( $this->usage['plan'] ) ? $this->usage['plan'] : $this->credentials['cloud_name'];
450+
// Stored option can be a non-array if it was corrupted by a previous
451+
// failed write. Reset to empty array to avoid string-offset fatals.
452+
if ( ! is_array( $history ) ) {
453+
$history = array();
454+
}
455+
$plan_source = is_array( $this->usage ) && ! empty( $this->usage['plan'] ) ? $this->usage['plan'] : '';
456+
$plan = '' !== $plan_source ? $plan_source : ( isset( $this->credentials['cloud_name'] ) ? $this->credentials['cloud_name'] : '' );
451457
for ( $i = 1; $i <= $days; $i++ ) {
452458
$date = date_i18n( 'd-m-Y', strtotime( '- ' . $i . ' days' ) );
453459
if ( ! isset( $history[ $plan ][ $date ] ) || is_wp_error( $history[ $plan ][ $date ] ) ) {
@@ -720,9 +726,14 @@ public function usage_stats( $refresh = false ) {
720726
$last_usage = $this->settings->get_setting( 'last_usage' );
721727
// Get users plan.
722728
$stats = $this->api->usage();
723-
if ( ! is_wp_error( $stats ) && ! empty( $stats['media_limits'] ) ) {
724-
$stats['max_image_size'] = $stats['media_limits']['image_max_size_bytes'];
725-
$stats['max_video_size'] = $stats['media_limits']['video_max_size_bytes'];
729+
if (
730+
! is_wp_error( $stats )
731+
&& is_array( $stats )
732+
&& isset( $stats['media_limits'] )
733+
&& is_array( $stats['media_limits'] )
734+
) {
735+
$stats['max_image_size'] = isset( $stats['media_limits']['image_max_size_bytes'] ) ? $stats['media_limits']['image_max_size_bytes'] : 0;
736+
$stats['max_video_size'] = isset( $stats['media_limits']['video_max_size_bytes'] ) ? $stats['media_limits']['video_max_size_bytes'] : 0;
726737
$last_usage->save_value( $stats );// Save the last successful call to prevgent crashing.
727738
} else {
728739
// Handle error by logging and fetching the last success.

‎php/class-cron.php‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,15 @@ public function rest_endpoints( $endpoints ) {
203203
*/
204204
protected function load_schedule() {
205205
$this->schedule = get_option( self::CRON_META_KEY, array() );
206+
// Guard against a corrupted option (e.g. a string was previously
207+
// stored) so the foreach below cannot fatal on PHP 8.x.
208+
if ( ! is_array( $this->schedule ) ) {
209+
$this->schedule = array();
210+
}
206211
foreach ( $this->schedule as &$item ) {
207-
$item['active'] = false;
212+
if ( is_array( $item ) ) {
213+
$item['active'] = false;
214+
}
208215
}
209216
}
210217

0 commit comments

Comments
 (0)