Skip to content
Open
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
1 change: 1 addition & 0 deletions .github/workflows/code_coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ jobs:

- name: Run codacy-coverage-reporter
uses: codacy/codacy-coverage-reporter-action@v1
if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
with:
project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
coverage-reports: tests/report/coverage.clover
4 changes: 2 additions & 2 deletions Tests/Integration/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@ protected function setPropertyValue( $property, $class, $value ) {
$ref->setValue( $class, $value );
} else {
$previous = $ref->getValue();
// Static property.
$ref->setValue( $value );
// Static property. Pass null as the object argument to avoid PHP 8.4 deprecation.
$ref->setValue( null, $value );
}

return $previous;
Expand Down
5 changes: 5 additions & 0 deletions Tests/Integration/inc/classes/ImagifyUser/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ abstract class TestCase extends BaseTestCase {
public function set_up() {
parent::set_up();

// Skip API-dependent tests when no API key is configured (e.g. fork PRs without repo secrets).
if ( '' === $this->getApiCredential( 'IMAGIFY_TESTS_API_KEY' ) ) {
$this->markTestSkipped( 'IMAGIFY_TESTS_API_KEY is not configured.' );
}

$this->originalUserInstance = $this->resetPropertyValue( 'user', Imagify::class );

//Clean up the transients for API cache
Expand Down
2 changes: 2 additions & 0 deletions Tests/e2e/specs/button-icon-alignment.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ test.describe( 'Button icon alignment (WP 7.0 compat)', () => {
await loginAsAdmin( page );
} );

test.skip( ! process.env.IMAGIFY_TESTS_API_KEY, 'IMAGIFY_TESTS_API_KEY not set — settings page dashicons require valid API key.' );
test( 'Settings page submit button dashicon has line-height: inherit', async ( { page } ) => {
await page.goto( '/wp-admin/options-general.php?page=imagify' );
await page.waitForLoadState( 'networkidle' );
Expand Down Expand Up @@ -56,6 +57,7 @@ test.describe( 'Button icon alignment (WP 7.0 compat)', () => {
await screenshotElement( page, 'button-icon-alignment-settings', dashiconInButton );
} );

test.skip( ! process.env.IMAGIFY_TESTS_API_KEY, 'IMAGIFY_TESTS_API_KEY not set — settings page dashicons require valid API key.' );
test( 'Imagify button dashicons have vertical-align: middle', async ( { page } ) => {
await page.goto( '/wp-admin/options-general.php?page=imagify' );
await page.waitForLoadState( 'networkidle' );
Expand Down
1 change: 1 addition & 0 deletions Tests/e2e/specs/generate-webp-button-responsive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ test.describe( 'Generate missing Next-Gen button — responsive layout (#1045)',
} );

async function gotoSettingsAndGetButton( page: Page ) {
test.skip( ! process.env.IMAGIFY_TESTS_API_KEY, 'IMAGIFY_TESTS_API_KEY not set — .generate-missing-webp section only renders with valid API key.' );
await page.goto( '/wp-admin/options-general.php?page=imagify', {
waitUntil: 'networkidle',
} );
Expand Down
21 changes: 2 additions & 19 deletions Tests/e2e/specs/htaccess-notice-dedup.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ test.describe( 'Issue #883 — .htaccess not writable: single error notice', ()
// Core regression — only ONE notice when .htaccess is read-only
// -----------------------------------------------------------------------

test.skip( ! process.env.IMAGIFY_TESTS_API_KEY, 'IMAGIFY_TESTS_API_KEY not set — WebP section only renders with valid API key.' );
test( 'Enabling display_nextgen with read-only .htaccess shows at most one error notice', async ( { page } ) => {
const settings = new SettingsPage( page );
await settings.goto();
Expand All @@ -60,25 +61,7 @@ test.describe( 'Issue #883 — .htaccess not writable: single error notice', ()
// Imagify uses a custom toggle: the label overlays the checkbox so we
// click the label (for="imagify_display_nextgen") rather than the input.
const displayNextgenCheckbox = page.locator( '[name="imagify_settings[display_nextgen]"]' ).first();
const checkboxCount = await displayNextgenCheckbox.count();

if ( checkboxCount === 0 ) {
// No API key — the webp section is hidden. Test what we can.
test.info().annotations.push( {
type: 'skip-reason',
description: 'display_nextgen checkbox not visible (API key not configured — WebP section hidden)',
} );

// Still verify that saving without changes produces at most one notice block.
await settings.saveButton.click();
await page.waitForLoadState( 'networkidle' );

await settings.expectNoFatalError();
await expect( page ).toHaveURL( /page=imagify/ );

await page.screenshot( { path: '.e2e-screenshots/htaccess-02-save-no-key.png' } );
return;
}
await expect( displayNextgenCheckbox ).toBeVisible( { timeout: 10_000 } );

// Ensure the checkbox is checked (enabled). Click the label which overlays
// the input — using force:true bypasses the intercepting label.
Expand Down
28 changes: 3 additions & 25 deletions Tests/e2e/specs/reset-internal-state.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,8 @@ test.describe( 'Reset Internal State — Troubleshooting section', () => {
await loginAsAdmin( page );
} );

test.skip( ! process.env.IMAGIFY_TESTS_API_KEY, 'IMAGIFY_TESTS_API_KEY not set — section only renders with valid API key.' );
test( 'Troubleshooting section is visible on settings page (requires API key)', async ( { page } ) => {
if ( ! process.env.IMAGIFY_TESTS_API_KEY ) {
// Hard-fail with an informative message instead of silently skipping.
expect(
process.env.IMAGIFY_TESTS_API_KEY,
'IMAGIFY_TESTS_API_KEY env var must be set — the Troubleshooting section is only rendered when a valid API key is configured. Set this variable to a valid key and re-run.'
).toBeTruthy();
return;
}

const settings = new SettingsPage( page );
await settings.goto();

Expand All @@ -38,15 +30,8 @@ test.describe( 'Reset Internal State — Troubleshooting section', () => {
await screenshotElement( page, 'reset-internal-state-section', section );
} );

test.skip( ! process.env.IMAGIFY_TESTS_API_KEY, 'IMAGIFY_TESTS_API_KEY not set — section only renders with valid API key.' );
test( 'Reset Internal State button is present and has a nonce attribute (requires API key)', async ( { page } ) => {
if ( ! process.env.IMAGIFY_TESTS_API_KEY ) {
expect(
process.env.IMAGIFY_TESTS_API_KEY,
'IMAGIFY_TESTS_API_KEY env var must be set — the Reset Internal State button is only rendered when a valid API key is configured.'
).toBeTruthy();
return;
}

const settings = new SettingsPage( page );
await settings.goto();

Expand All @@ -60,15 +45,8 @@ test.describe( 'Reset Internal State — Troubleshooting section', () => {
await screenshotElement( page, 'reset-internal-state-button', button );
} );

test.skip( ! process.env.IMAGIFY_TESTS_API_KEY, 'IMAGIFY_TESTS_API_KEY not set — section only renders with valid API key.' );
test( 'Reset Internal State button is clickable (requires API key)', async ( { page } ) => {
if ( ! process.env.IMAGIFY_TESTS_API_KEY ) {
expect(
process.env.IMAGIFY_TESTS_API_KEY,
'IMAGIFY_TESTS_API_KEY env var must be set — the Reset Internal State button is only rendered when a valid API key is configured.'
).toBeTruthy();
return;
}

const settings = new SettingsPage( page );
await settings.goto();

Expand Down
22 changes: 19 additions & 3 deletions inc/admin/meta-boxes.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,37 @@ function _imagify_attachment_submitbox_misc_actions() {
}
?>
<div class="misc-pub-section misc-pub-imagify">
<?php $views->print_template( 'button/processing', [ 'label' => $lock_label ] ); ?>
<div class="imagify-data-actions-container" data-id="<?php echo esc_attr( $post->ID ); ?>" data-context="wp">
<?php $views->print_template( 'button/processing', [ 'label' => $lock_label ] ); ?>
</div>
</div>
<?php
} elseif ( $data->is_optimized() || $data->is_already_optimized() || $data->is_error() ) {
?>
<div class="misc-pub-section misc-pub-imagify"><h4><?php esc_html_e( 'Imagify', 'imagify' ); ?></h4></div>
<div class="misc-pub-section misc-pub-imagify imagify-data-item">
<?php echo get_imagify_attachment_optimization_text( $process ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<div class="imagify-data-actions-container" data-id="<?php echo esc_attr( $post->ID ); ?>" data-context="wp">
<?php echo get_imagify_attachment_optimization_text( $process ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
</div>
<?php
} else {
$url = get_imagify_admin_url( 'optimize', [ 'attachment_id' => $post->ID ] );
?>
<div class="misc-pub-section misc-pub-imagify">
<a class="button-primary" href="<?php echo esc_url( $url ); ?>"><?php esc_html_e( 'Optimize', 'imagify' ); ?></a>
<div class="imagify-data-actions-container" data-id="<?php echo esc_attr( $post->ID ); ?>" data-context="wp">
<?php
$views->print_template(
'button/optimize',
[
'url' => $url,
'atts' => [
'class' => 'button-primary button-imagify-optimize',
],
]
);
?>
</div>
</div>
<?php
}
Expand Down
10 changes: 10 additions & 0 deletions inc/classes/class-imagify-assets.php
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,16 @@ public function enqueue_styles_and_scripts() {
$this->enqueue_assets( 'twentytwenty' );
}

/**
* Loaded in the attachment edition: AJAX-ify the optimize/restore buttons.
*/
if ( imagify_is_screen( 'attachment' ) ) {
$this->enqueue_script( 'media-modal' );

// The optimization buttons fire AJAX requests from JS, so the processing template must be printed in the footer.
Imagify_Views::get_instance()->print_js_template_in_footer( 'button/processing' );
}

/**
* Loaded in the library.
*/
Expand Down
18 changes: 18 additions & 0 deletions inc/main.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,24 @@
require_once IMAGIFY_PATH . 'vendor/autoload.php';
}

// Support Composer dependency install where Strauss prefixing hasn't run.
// Prefixed classes exist when installed as root package; unprefixed when installed as dependency.
if ( ! class_exists( 'Imagify\Dependencies\League\Container\Container' ) ) {
if ( class_exists( 'League\Container\Container' ) ) {
class_alias( 'League\Container\Container', 'Imagify\Dependencies\League\Container\Container' );
}
}
if ( ! interface_exists( 'Imagify\Dependencies\League\Container\ServiceProvider\ServiceProviderInterface' ) ) {
if ( interface_exists( 'League\Container\ServiceProvider\ServiceProviderInterface' ) ) {
class_alias( 'League\Container\ServiceProvider\ServiceProviderInterface', 'Imagify\Dependencies\League\Container\ServiceProvider\ServiceProviderInterface' );
}
}
if ( ! class_exists( 'Imagify\Dependencies\League\Container\ServiceProvider\AbstractServiceProvider' ) ) {
if ( class_exists( 'League\Container\ServiceProvider\AbstractServiceProvider' ) ) {
class_alias( 'League\Container\ServiceProvider\AbstractServiceProvider', 'Imagify\Dependencies\League\Container\ServiceProvider\AbstractServiceProvider' );
}
}

require_once IMAGIFY_PATH . 'inc/Dependencies/ActionScheduler/action-scheduler.php';

/**
Expand Down
Loading