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
4 changes: 4 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ jobs:
'EndToEnd/forms/general',
'EndToEnd/forms/post-types',
'EndToEnd/general/other',
'EndToEnd/general/uninstall',
'EndToEnd/general/plugin-screens',
'EndToEnd/integrations/divi-builder',
'EndToEnd/integrations/divi-theme',
Expand Down Expand Up @@ -187,7 +188,10 @@ jobs:

# DISALLOW_FILE_MODS = true is required to disable the block directory's "Available to Install" suggestions, which trips up
# tests that search and wait for block results.
# We don't enable DISALLOW_FILE_MODS for the UninstallCest test, as tests will perform a Plugin deletion
# which requires DISALLOW_FILE_MODS to not be true.
- name: Enable DISALLOW_FILE_MODS
if: ${{ matrix.test-groups != 'EndToEnd/general/uninstall' }}
working-directory: ${{ env.ROOT_DIR }}
run: |
wp-cli config set DISALLOW_FILE_MODS true --raw
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,6 @@ public function testPluginActivationAndDeactivationWithOtherPlugins(EndToEndTest
// Deactivate Plugins.
$I->deactivateThirdPartyPlugin($I, 'convertkit-for-woocommerce');
$I->deactivateKitPlugin($I);
$I->resetKitPlugin($I);
}
}
106 changes: 106 additions & 0 deletions tests/EndToEnd/general/uninstall/UninstallCest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php

namespace Tests\EndToEnd;

use Tests\Support\EndToEndTester;

/**
* Tests Plugin uninstallation.
*
* @since 3.2.4
*/
class UninstallCest
{
/**
* Test that the Plugin's access and refresh tokens are revoked, and all v4 and v4
* API credentials are removed from the Plugin's settings when the Plugin is deleted.
*
* @since 3.2.4
*
* @param EndToEndTester $I Tester.
*/
public function testPluginDeletionRevokesAndRemovesTokens(EndToEndTester $I)
{
// Activate this Plugin.
$I->activateKitPlugin($I);

// Generate an access token and refresh token by API key and secret.
// We don't use the tokens from the environment, as revoking those
// would result in later tests failing.
$result = wp_remote_post(
'https://api.kit.com/wordpress/accounts/oauth_access_token',
[
'headers' => [
'Content-Type' => 'application/json',
],
'body' => wp_json_encode(
[
'api_key' => $_ENV['CONVERTKIT_API_KEY'],
'api_secret' => $_ENV['CONVERTKIT_API_SECRET'],
'client_id' => $_ENV['CONVERTKIT_OAUTH_CLIENT_ID'],
'tenant_name' => wp_generate_password( 10, false ), // Random tenant name to produce a token for this request only.
]
),
]
);
$tokens = json_decode(wp_remote_retrieve_body($result), true)['oauth'];

// Store the tokens and API keys in the Plugin's settings.
$I->setupKitPlugin(
$I,
[
'access_token' => $tokens['access_token'],
'refresh_token' => $tokens['refresh_token'],
'token_expires' => $tokens['expires_at'],
'api_key' => $_ENV['CONVERTKIT_API_KEY'],
'api_secret' => $_ENV['CONVERTKIT_API_SECRET'],
]
);

// Deactivate the Plugin.
$I->deactivateKitPlugin($I);

// Delete the Plugin.
$I->deleteKitPlugin($I);

// Confirm the credentials have been removed from the Plugin's settings.
$I->wait(3);
$settings = $I->grabOptionFromDatabase('_wp_convertkit_settings');
$I->assertEmpty($settings['access_token']);
$I->assertEmpty($settings['refresh_token']);
$I->assertEmpty($settings['token_expires']);
$I->assertEmpty($settings['api_key']);
$I->assertEmpty($settings['api_secret']);

// Confirm attempting to use the revoked access token no longer works.
$result = wp_remote_get(
'https://api.kit.com/v4/account',
[
'headers' => [
'Authorization' => 'Bearer ' . $tokens['access_token'],
],
]
);
$data = json_decode(wp_remote_retrieve_body($result), true);
$I->assertArrayHasKey( 'errors', $data );
$I->assertEquals( 'The access token was revoked', $data['errors'][0] );

// Confirm attempting to use the revoked refresh token no longer works.
$result = wp_remote_post(
'https://api.kit.com/v4/oauth/token',
[
'headers' => [
'Authorization' => 'Bearer ' . $tokens['access_token'],
],
'body' => [
'client_id' => $_ENV['CONVERTKIT_OAUTH_CLIENT_ID'],
'grant_type' => 'refresh_token',
'refresh_token' => $tokens['refresh_token'],
],
]
);
$data = json_decode(wp_remote_retrieve_body($result), true);
$I->assertArrayHasKey( 'error', $data );
$I->assertEquals( 'invalid_grant', $data['error'] );
}
}
13 changes: 13 additions & 0 deletions tests/Support/Helper/KitPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,19 @@ public function deactivateKitPlugin($I)
$I->deactivateThirdPartyPlugin($I, 'convertkit');
}

/**
* Helper method to delete the Kit Plugin, checking
* it deleted and no errors were output.
*
* @since 3.2.4
*
* @param EndToEndTester $I EndToEndTester.
*/
public function deleteKitPlugin($I)
{
$I->deleteThirdPartyPlugin($I, 'convertkit');
}

/**
* Helper method to programmatically setup the Plugin's settings, as if the
* user configured the Plugin at `Settings > Kit`.
Expand Down
33 changes: 33 additions & 0 deletions tests/Support/Helper/ThirdPartyPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,39 @@ public function deactivateThirdPartyPlugin($I, $name)
}
}

/**
* Helper method to delete a third party Plugin, checking
* it deleted and no errors were output.
*
* @since 3.2.4
*
* @param EndToEndTester $I EndToEnd Tester.
* @param string $name Plugin Slug.
*/
public function deleteThirdPartyPlugin($I, $name)
{
// Login as the Administrator, if we're not already logged in.
if ( ! $this->amLoggedInAsAdmin($I) ) {
$this->doLoginAsAdmin($I);
}

// Go to the Plugins screen in the WordPress Administration interface.
$I->amOnPluginsPage();

// Wait for the Plugins page to load.
$I->waitForElementVisible('body.plugins-php');

// Delete the Plugin.
$I->waitForElementVisible('a#delete-' . $name);
$I->click('a#delete-' . $name);

// Click the confirmation dialog.
$I->acceptPopup();

// Wait for the Plugin to be marked as deleted.
$I->waitForElementNotVisible('table.plugins tr.deleted[data-slug=' . $name . ']');
}

/**
* Helper method to check if the Administrator is logged in.
*
Expand Down
75 changes: 75 additions & 0 deletions uninstall.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php
/**
* Uninstall routine. Runs when the Plugin is deleted
* at Plugins > Delete.
*
* @package ConvertKit
* @author ConvertKit
*/

// If uninstall.php is not called by WordPress, die.
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
die;
}

// Only WordPress and PHP methods can be used. Plugin classes and methods
// are not reliably available due to the Plugin being deactivated and going
// through deletion now.

// Get settings.
$settings = get_option( '_wp_convertkit_settings' );

// Bail if no settings exist.
if ( ! $settings ) {
return;
}

// Revoke Access Token.
if ( array_key_exists( 'access_token', $settings ) && ! empty( $settings['access_token'] ) ) {
wp_remote_post(
'https://api.kit.com/v4/oauth/revoke',
array(
'headers' => array(
'Accept' => 'application/json',
'Content-Type' => 'application/json',
),
'body' => wp_json_encode(
array(
'client_id' => 'HXZlOCj-K5r0ufuWCtyoyo3f688VmMAYSsKg1eGvw0Y',
'token' => $settings['access_token'],
)
),
'timeout' => 5,
)
);
}

// Revoke Refresh Token.
if ( array_key_exists( 'refresh_token', $settings ) && ! empty( $settings['refresh_token'] ) ) {
wp_remote_post(
'https://api.kit.com/v4/oauth/revoke',
array(
'headers' => array(
'Accept' => 'application/json',
'Content-Type' => 'application/json',
),
'body' => wp_json_encode(
array(
'client_id' => 'HXZlOCj-K5r0ufuWCtyoyo3f688VmMAYSsKg1eGvw0Y',
'token' => $settings['refresh_token'],
)
),
'timeout' => 5,
)
);
}

// Remove credentials from settings.
$settings['access_token'] = '';
$settings['refresh_token'] = '';
$settings['token_expires'] = '';
$settings['api_key'] = '';
$settings['api_secret'] = '';

// Save settings.
update_option( '_wp_convertkit_settings', $settings );
Loading