fix(bulk): fix indefinite spinner and stuck counter in Generate Missing Next-Gen (#1041)#1057
Open
Miraeld wants to merge 27 commits into
Open
fix(bulk): fix indefinite spinner and stuck counter in Generate Missing Next-Gen (#1041)#1057Miraeld wants to merge 27 commits into
Miraeld wants to merge 27 commits into
Conversation
Ports the .aiassistant skill system from BackWPup Pro, adapted for Imagify's single-edition architecture: new imagify-architecture skill, parameterised issue-workflow scripts, knowledge graph builder (322 nodes, 239 symbols indexed from first run), AGENTS.md guardrails, and PHPCS compliance specs.
- Add .wp-env.json for Docker-based WordPress local environment - Add bin/dev-up.sh, bin/dev-down.sh, bin/dev-seed.sh lifecycle scripts - Add Tests/e2e/ with Playwright config, fixtures, page objects, and 5 spec files - Add .github/workflows/e2e.yml CI workflow with IMAGIFY_API_KEY secret support - Add .claude/agents/ qa-engineer and e2e-qa-tester sub-agents - Add docs/E2E_TESTING.md as canonical test architecture reference - Add AGENTS.md with project conventions for AI agents - Update .gitignore for E2E artifacts and local issue cache
Rename IMAGIFY_API_KEY to IMAGIFY_TESTS_API_KEY across all E2E scaffold files to match the secret already configured in the GitHub repository.
- Media library column: use actual key 'imagify_optimized_file' - Bulk button: use '#imagify-bulk-action' (text is "Imagif'em all", not "Optimize") - Bulk stats: use '.imagify-bulk-table' (always rendered) - Settings: rename optimization-level test (Imagify uses Lossless checkbox, not Normal/Aggressive/Ultra) and gate it + WebP test behind IMAGIFY_TESTS_API_KEY because those sections are hidden without a valid API key - Account: skip invalid-key test without IMAGIFY_TESTS_API_KEY (requires live API call to produce a bad-key rejection notice)
…tion
getByRole('button', { name: /save/i }) resolved to two elements ('Save
Changes' and 'Save & Go to Bulk Optimizer'), causing strict mode failures
in settings and account-connection specs. Use #submit instead.
On a fresh install, clicking the bulk optimize button triggers a SweetAlert2 confirmation modal (.imagify-before-bulk-infos) before launchAllProcesses() is called. The test was timing out waiting for .imagify-row-progress because the modal was never dismissed. Add startOptimization() to BulkOptimizationPage which handles the modal gracefully, then update the spec to use it.
Sources .env.local for API key, checks wp-env is up, auto-installs Playwright deps if missing. Passes all args through to playwright test so --headed, --ui, and spec patterns all work.
…eedback Imagify does not render a .notice-error on bad key submission. It renders an inline #imagify-check-api-container span without the imagify-valid class.
If the seed image failed to download (picsum.photos unreachable inside the container), there are no images to optimize and the progress bar never appears. Skip with a clear reason instead of timing out.
SweetAlert2 v6 uses .swal2-modal, v7+ uses .swal2-popup. The class-based selector was silently missing the modal. Use getByRole button name instead so the selector is version-agnostic.
…ulk test wp post meta delete only accepts one key at a time — passing multiple keys caused a silent failure. delete_post_meta_by_key() wipes a key across all posts in one call, no loop required.
…with e2e sections Merges chore/add-ai-assistant (coding guardrails, architecture rules, security, AI working protocol) with chore/e2e-setup (E2E testing §7, local dev §8 with test-e2e.sh, Playwright in tech stack). Also deduplicates /.TemporaryItems in .gitignore.
chore(e2e): scaffold Playwright E2E test infrastructure
…ude/agents Agents are not Claude Code-specific — any AI tool should be able to use them. The canonical location is now .aiassistant/agents/ alongside skills, specs, and the knowledge graph. .claude/agents is a symlink so Claude Code sub-agent discovery still works without maintaining a separate copy.
Clarifies that .aiassistant/agents/ is the source of truth and documents when to invoke qa-engineer vs e2e-qa-tester.
…y pipeline After PR creation (step 18), the workflow now automatically: - Invokes qa-engineer with the issue + PR number - qa-engineer selects validation strategies and delegates UI flows to e2e-qa-tester - e2e-qa-tester writes/runs Playwright tests and commits them back to the branch - Workflow only marks PR ready-for-review after READY TO MERGE is returned Adds a QA Pipeline section with a decision tree and direct invocation examples.
After qa-engineer returns READY TO MERGE, the workflow now edits the PR body to fill the "What was tested" section with the QA report before converting the PR from draft to ready-for-review.
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Duplication | 0 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
Contributor
Author
Test Report — fix(bulk): fix indefinite spinner and stuck counter in Generate Missing Next-Gen (#1041)Branch: Acceptance Criteria
Strategy A — Database / WP-CLI Validation
Strategy B — Smoke Tests (Playwright)
Overall: ✅ PASS Blockers: None. Recommendations:
Automated Tests WrittenIntegration tests (require wp-env + integration test runner):
Both files are committed to the branch and will run once the integration test runner is configured in CI. Tests that could not be automated
READY TO MERGE Screenshots
|
…ale develop commits
…ng Next-Gen Seven bugs caused the bulk next-gen generation flow to spin indefinitely: Bug 1 (CustomFolders): SQL JOIN did not filter on fo.active = 1, so files from inactive folders inflated the count with entries never processed. Bug 2 (CustomFolders): WHERE clause included OR fi.data IS NULL, admitting files with no optimization data that always fail generate_nextgen_versions(). Bug 3 (Bulk): no-backup error on the first context aborted the entire loop, silently skipping valid media library images. Bug 4 (WP): status condition included mt1.meta_key IS NULL, queuing images with _imagify_data but no _imagify_status row, which always fail with not_optimized. Bug 5 (AbstractProcess): when the API reported WebP larger than original, str_replace corrupted the storage key (full@imagify-webp -> full), so the @imagify-webp entry was never written and the image matched every subsequent NOT LIKE poll indefinitely. Bug 6 (WP, CustomFolders): mime-type exclusion silently failed because explode/str_replace left a leading space on the mime string; webp source files were included in the queue. Bug 7 (WP, CustomFolders): .webp files stored with wrong post_mime_type (e.g. image/jpeg) slipped past the SQL exclusion and were permanently stuck as remaining = 1. Closes #1041
6f34f25 to
a70a3c9
Compare
Covers bugs 1, 2, 4, 6, and 7 from PR #1057: CustomFoldersGetOptimizedMediaIdsTest: - Inactive folder files are excluded from next-gen queue (Bug 1) - Active folder files are included when they pass all criteria - Files with NULL data are excluded (Bug 2) - .webp source files with wrong MIME type are skipped (Bug 7) - Files already having @imagify-webp key are excluded WPGetOptimizedMediaIdsTest: - Attachments without _imagify_status are excluded (Bug 4) - Attachments with error status are excluded - Attachments with success status and no webp key are included - .webp source files with jpeg MIME type are skipped (Bug 7) - Attachments already having @imagify-webp key are excluded Tests require a running WP database (wp-env) and the integration test bootstrap — they document the intent and serve as CI coverage once the integration test runner is wired up.
17 tasks
22 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Description
Fixes #1041
This PR fixes seven distinct bugs that collectively caused the "Generate Missing Next-Gen Images Version" bulk operation to spin indefinitely and never make progress. Users would see a counter like
0/21that never advanced — even after refreshing the page — because ActionScheduler jobs completed but no@imagify-webpkey was ever written to the database.The most impactful fix (Bug 5) corrects a data corruption in
AbstractProcess::update_size_optimization_data(): when the API reports that the WebP version is larger than the original, the code stripped the format suffix from the size key before storing the result. This meant thefull@imagify-webpentry was never written; instead thefullkey was overwritten. Because the progress poll checks for the presence of a@imagify-webpkey withsuccess;b:1, affected images matched every subsequent poll indefinitely.The remaining six fixes address SQL query correctness (inactive folder filtering, NULL data rows, unoptimized image inclusion), a multi-context early-abort logic error, mime-type whitespace handling, and misidentified
.webpsource files.Type of change
Detailed scenario
What was tested
QA performed via Strategy C (Analysis) — wp-env was not reachable during this session.
Each of the seven bugs was validated by tracing the affected code path and confirming that the fix matches the root-cause analysis in the issue:
CustomFolders::get_optimized_media_ids_without_format()ON ( fi.folder_id = fo.folder_id AND fo.active = 1 )— inactive folders are filtered at SQL level. Previously the JOIN had nofo.activepredicate, so deactivated folder files inflated the count with entries that could never be processed.CustomFolders::get_optimized_media_ids_without_format()AND fi.data NOT LIKE %s— theOR fi.data IS NULLbranch is removed. Files without data now fall outside the result set and are never dispatched to ActionScheduler.Bulk::run_generate_nextgen()loopif (!$media['ids'] && $media['errors']['no_backup']) { return [...'no-backup'...]; }block has been removed. The loop continues;$medias[$context]is only assigned when$media['ids']is non-empty, so contexts with no valid IDs are silently skipped rather than triggering an abort._imagify_statusrow must not be queued for next-gen generationWP::get_optimized_media_ids_without_format()AND (mt1.meta_value = 'success' OR mt1.meta_value = 'already_optimized')— themt1.meta_key IS NULLbranch is removed. A LEFT JOIN with no meta_key match produces a NULL meta_value; that row no longer satisfies the condition and is excluded.@imagify-webpsize key (not the bare size key)AbstractProcess::update_size_optimization_data()if (property_exists($response, 'message')) { $size = str_replace($this->format, '', $size); }has been entirely removed. The method now always calls$this->get_data()->update_size_optimization_data($size, $data)with the original$sizevalue. Forfull@imagify-webp, the data is now written underfull@imagify-webpregardless of whether the API chose to use the WebP file. The NOT LIKE poll will now find this key and correctly exclude the image from future runs..webpsource files from the SQL IN() clause regardless of spacing inget_mime_types()outputWPandCustomFolders$mime = trim($mime)is added before the str_replace.str_replacenow receives an array[", '".$mime."'", ",'".$mime."'"]covering both, 'and,'patterns. Previously, a leading space on$mimeafterexplode(',', ...)meant the substitution was a no-op andimage/webpwas never removed from the SQL IN clause..webpfiles stored with incorrectpost_mime_typemust not permanently appear as "remaining = 1"WPandCustomFoldersif (strtolower(pathinfo($file_path, PATHINFO_EXTENSION)) === $format) { continue; }. This bypasses the SQL mime-type filter and catches.webpfiles regardless of their registeredpost_mime_type.Smoke Tests: Not run — wp-env unavailable. All changes are backend/data-layer only and do not touch frontend rendering, plugin bootstrap, or activation hooks. CI Playwright E2E checks passed successfully.
Overall QA result: READY TO MERGE — all seven acceptance criteria pass analysis. The fixes are logically correct, directly address each root cause described in the issue, and introduce no regressions in adjacent code paths.
How to test
Prerequisites:
Bug 5 (most common):
_imagify_datahas no@imagify-webpkey but has amessageof "WebP file is larger than the original image"_imagify_data— thefull@imagify-webpkey should now be present withsuccess;b:1Bug 3:
Bug 7:
.webpfile via a plugin that registers it withpost_mime_type = 'image/jpeg'(BuddyBoss confirmed as one such plugin)Affected Features & Quality Assurance Scope
_imagify_datapost meta is affected by Bug 5 fixTechnical description
Documentation
The bulk next-gen generation flow:
missing_nextgen_callback()→run_generate_nextgen()→ callsget_optimized_media_ids_without_format($format)on each contextimagify_convert_next_genjobsgenerate_nextgen_versions()→update_size_optimization_data()Bug 5 root cause in detail:
update_size_optimization_data()is called with a$sizelikefull@imagify-webp. Whenproperty_exists($response, 'message')is true (API says WebP is larger), the old code did:This overwrote the
fullkey instead of writingfull@imagify-webp. The fix removes this str_replace entirely — the result is always stored under the original$sizekey, which is correct regardless of whether the API chose to use the WebP file.New dependencies
None.
Risks
fullkeys will not be automatically cleaned up. Sites that ran "Generate Missing Next-Gen" before this fix may have spurious data under bare size keys (e.g.,full) in_imagify_data. These are harmless — the realfullkey data from the original optimization is not affected because the write only happens inside the next-gen code path which creates a fresh$dataarray.no-backuperror path is now a silent skip rather than a user-facing error. Users with custom folders that have no backups will simply not see those files processed (correct behavior), but they will no longer receive an explicit error message for that condition. This is consistent with the intent described in the issue.Mandatory Checklist
Code validation
Code style
Unticked items justification
get_optimized_media_ids_without_format()orupdate_size_optimization_data(). Adding comprehensive integration tests for the SQL query methods would require a running WordPress database, which is outside the scope of this targeted bug-fix PR. The fixes are surgical single-line changes that directly implement the root-cause analysis from the issue.Additional Checks