Skip to content
Merged
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/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ jobs:
'EndToEnd/forms',
'EndToEnd/general',
'EndToEnd/recommendations',
'EndToEnd/uninstall',
'Integration'
]

Expand Down
103 changes: 103 additions & 0 deletions tests/EndToEnd/uninstall/UninstallCest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php

namespace Tests\EndToEnd;

use Tests\Support\EndToEndTester;

/**
* Tests Plugin uninstallation.
*
* @since 1.9.2
*/
class UninstallCest
{
/**
* Test that the Plugin's access and refresh tokens are revoked, and all v4 and v3
* API credentials are removed from the Plugin's settings when the Plugin is deleted.
*
* @since 1.9.2
*
* @param EndToEndTester $I Tester.
*/
public function testPluginDeletionRevokesAndRemovesTokens(EndToEndTester $I)
{
// Activate this Plugin.
$I->activateConvertKitPlugin($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->setupWPFormsIntegration(
$I,
accessToken: $tokens['access_token'],
refreshToken: $tokens['refresh_token'],
apiKey: $_ENV['CONVERTKIT_API_KEY'],
apiSecret: $_ENV['CONVERTKIT_API_SECRET']
);

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

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

// Confirm the credentials have been removed from the Plugin's settings.
$I->wait(3);
$settings = $I->grabOptionFromDatabase('wpforms_providers');
$connection = reset($settings['convertkit']);
$I->assertEmpty($connection['access_token']);
$I->assertEmpty($connection['refresh_token']);
$I->assertEmpty($connection['api_key']);
$I->assertEmpty($connection['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'] );
}
}
12 changes: 12 additions & 0 deletions tests/Support/Helper/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ public function deactivateConvertKitPlugin($I)
$I->deactivateThirdPartyPlugin($I, 'integrate-convertkit-wpforms');
}

/**
* Helper method to delete the Kit Plugin.
*
* @since 1.9.2
*
* @param EndToEndTester $I EndToEndTester.
*/
public function deleteKitPlugin($I)
{
$I->deleteThirdPartyPlugin($I, 'integrate-convertkit-wpforms');
Comment thread
n7studios marked this conversation as resolved.
}

/**
* Helper method to determine that the order of the Form resources in the given
* select element are in the expected alphabetical order.
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 @@ -74,6 +74,39 @@ public function deactivateThirdPartyPlugin($I, $name)
$I->waitForElementVisible('table.plugins tr[data-slug=' . $name . '].inactive');
}

/**
* Helper method to delete a third party Plugin, checking
* it deleted and no errors were output.
*
* @since 1.9.2
*
* @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
83 changes: 83 additions & 0 deletions uninstall.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php
/**
* Uninstall routine. Runs when the Plugin is deleted
* at Plugins > Delete.
*
* @package CKWC
* @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 providers.
$providers = get_option( 'wpforms_providers' );

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

// Bail if no Kit connections exist.
if ( ! array_key_exists( 'convertkit', $providers ) ) {
return;
}

// Iterate through each connection, revoking the tokens.
foreach ( $providers['convertkit'] as $account_id => $connection ) {
// Revoke Access Token.
if ( array_key_exists( 'access_token', $connection ) && ! empty( $connection['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' => 'L0kyADsB3WP5zO5MvUpXQU64gIntQg9BBAIme17r_7A',
'token' => $connection['access_token'],
)
),
'timeout' => 5,
)
);
}

// Revoke Refresh Token.
if ( array_key_exists( 'refresh_token', $connection ) && ! empty( $connection['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' => 'L0kyADsB3WP5zO5MvUpXQU64gIntQg9BBAIme17r_7A',
'token' => $connection['refresh_token'],
)
),
'timeout' => 5,
)
);
}

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

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