diff --git a/src/Plugin/GravityForms/Field.php b/src/Plugin/GravityForms/Field.php
index 09acbf8..f607cf4 100644
--- a/src/Plugin/GravityForms/Field.php
+++ b/src/Plugin/GravityForms/Field.php
@@ -9,16 +9,18 @@
use AdCaptcha\Widget\Verify;
use Exception;
-if (!class_exists('GF_Field')) {
+// update the condition for testing environment
+if (!class_exists('GF_Field') && !defined('PHPUNIT_RUNNING')) {
return;
}
class Field extends GF_Field {
- public $type = 'adcaptcha';
+ public $type;
private $verify;
public function __construct($data = []) {
parent::__construct($data);
+ $this->type = 'adcaptcha';
$this->verify = new Verify();
$this->setup();
}
@@ -64,7 +66,7 @@ public function add_to_field_groups($field_groups): array {
}
}
$field_groups['advanced_fields']['fields'][] = [
- 'data-type' => $this->type,
+ 'data-type' => (string) $this->type,
'value' => $this->get_form_editor_field_title(),
'label' => $this->get_form_editor_field_title(),
];
@@ -118,27 +120,18 @@ public function update_adcaptcha_label() {
if (!$forms || !is_array($forms)) {
return;
}
- $errors = [];
foreach ($forms as $form) {
$updated = false;
foreach ($form['fields'] as &$field) {
if ($field->type === 'adcaptcha' && $field->label !== __('adCAPTCHA', 'adcaptcha')) {
$field->label = __('adCAPTCHA', 'adcaptcha');
- $field['label'] = __('adCAPTCHA', 'adcaptcha');
$updated = true;
}
}
if ($updated) {
- $result = GFAPI::update_form($form);
-
- if (is_wp_error($result)) {
- $errors[] = "Failed to update adCAPTCHA label in Form ID {$form['id']}: " . $result->get_error_message();
- }
+ GFAPI::update_form($form);
}
}
- if (!empty($errors)) {
- throw new Exception(implode("\n", $errors));
- }
}
public function enqueue_admin_script() {
@@ -199,7 +192,10 @@ public function enqueue_preview_scripts($form_id) {
}
public function get_field_input($form, $value = '', $entry = null) {
- $form_id = $form['id'];
+ $form_id = $form['id'] ?? null;
+ if ($form_id === null) {
+ return '';
+ }
$field_id = (int) $this->id;
if ($this->is_form_editor()) {
return "
adCAPTCHA will be rendered here.
";
diff --git a/src/Plugin/GravityForms/Forms.php b/src/Plugin/GravityForms/Forms.php
index 88eaecd..dc1ad95 100644
--- a/src/Plugin/GravityForms/Forms.php
+++ b/src/Plugin/GravityForms/Forms.php
@@ -25,4 +25,3 @@ public function register_adcaptcha_field() {
}
}
}
-
diff --git a/tests/Plugin/GravityForms/GravityFormsTest.php b/tests/Plugin/GravityForms/GravityFormsTest.php
new file mode 100644
index 0000000..9d9c873
--- /dev/null
+++ b/tests/Plugin/GravityForms/GravityFormsTest.php
@@ -0,0 +1,441 @@
+type = $type;
+ $this->id = 123;
+ $this->isFormEditor = $isFormEditor;
+ }
+
+ public function get_field_label($input1 = false, $input2 = '') {
+ return 'AdCaptcha Label';
+ }
+
+ public function is_form_editor() {
+ return $this->isFormEditor;
+ }
+}
+
+ use PHPUnit\Framework\TestCase;
+ use AdCaptcha\Plugin\GravityForms\Forms;
+ use AdCaptcha\Plugin\GravityForms\Field;
+ use AdCaptcha\Widget\AdCaptcha;
+ use AdCaptcha\Widget\Verify;
+ use Brain\Monkey;
+ use Brain\Monkey\Functions;
+ use Mockery;
+ use ReflectionClass;
+
+ class GravityFormsTest extends TestCase {
+ private $forms;
+ private $verifyMock;
+ private $field;
+ private $mockGFAPI;
+
+ public function setUp(): void {
+ parent::setUp();
+ Monkey\setUp();
+
+ global $mocked_actions, $mocked_filters;
+ $mocked_actions = [];
+ $mocked_filters = [];
+
+ if (!class_exists('GF_Fields', false)) {
+ class_alias(MockGF_Fields::class, 'GF_Fields');
+ }
+ if(!class_exists('GF_Field', false)) {
+ class_alias(MockGF_Field::class, 'GF_Field');
+ }
+
+ Functions\when('esc_html__')->alias(function ($text, $domain) {
+ return "[{$domain}] {$text}";
+ });
+ Functions\when('esc_attr__')->alias(function ($text, $domain) {
+ return "[{$domain}] {$text}";
+ });
+ Functions\when('esc_js')->alias(function ($text) {
+ return $text;
+ });
+ Functions\when('esc_attr')->alias(function ($text) {
+ return $text;
+ });
+ Functions\when('get_option')->alias(function ($option_name) {
+ $mock_values = [
+ 'adcaptcha_placement_id' => 'mocked-placement-id',
+ ];
+ return $mock_values[$option_name] ?? null;
+ });
+ Functions\when('sanitize_text_field')->alias(function($input) {
+ $sanitized = strip_tags($input);
+ $sanitized = preg_replace('/[\r\n\t]/', ' ', $sanitized);
+ $sanitized = trim($sanitized);
+ return $sanitized;
+ });
+ Functions\when('__')->alias(function ($text, $domain = null) {
+ return $text;
+ });
+ Functions\when('plugin_dir_url')->alias(function ($path) {
+ return '';
+ });
+
+ $this->mockGFAPI = Mockery::mock('alias:GFAPI');
+ $this->forms = new Forms();
+ $this->field = new Field($this->mockGFAPI);
+
+ $this->verifyMock = $this->createMock(Verify::class);
+ $reflection = new \ReflectionClass($this->field);
+ $property = $reflection->getProperty('verify');
+ $property->setAccessible(true);
+ $property->setValue($this->field, $this->verifyMock);
+ }
+
+ public function tearDown(): void {
+ global $mocked_filters;
+ $mocked_filters = [];
+ Mockery::close();
+ Monkey\tearDown();
+ parent::tearDown();
+ }
+
+ // Tests if the 'setup' method exists and properly registers the 'gform_loaded' action with the expected parameters.
+ public function testSetup() {
+ $this->assertTrue(method_exists($this->forms, 'setup'), 'Method setup does not exist');
+ global $mocked_actions;
+ $this->forms->setup();
+ $registeredAction = array_filter($mocked_actions, function ($action) {
+ return $action['hook'] === 'gform_loaded';
+ });
+ $this->assertNotEmpty($registeredAction, 'The gform_loaded action was not registered.');
+ $registeredAction = array_values($registeredAction)[0];
+ $this->assertEquals(10, $registeredAction['priority'], 'Expected priority 10 for gform_loaded.');
+ $this->assertEquals(0, $registeredAction['accepted_args'], 'Expected 0 accepted args for gform_loaded.');
+ $this->assertInstanceOf(\Closure::class, $registeredAction['callback'], 'Expected a Closure as the callback.');
+ }
+
+ // Tests if the 'register_adcaptcha_field' method exists and returns null as expected.
+ public function testRegisterAdcaptchaField() {
+ $this->assertTrue(method_exists($this->forms, 'register_adcaptcha_field'), 'Method register_adcaptcha_field does not exist');
+ $result = $this->forms->register_adcaptcha_field();
+ $this->assertNull($result, 'Expected null return value from register_adcaptcha_field');
+ }
+
+ // Tests if the 'setup_hooks' method exists and correctly registers expected actions and filters.
+ public function testSetupHooks() {
+ $this->assertTrue(method_exists($this->field, 'setup_hooks'), 'Method setup_hooks does not exist');
+
+ global $mocked_actions, $mocked_filters;
+ $this->assertContains(
+ ['hook' => 'wp_enqueue_scripts', 'callback' => [AdCaptcha::class, 'enqueue_scripts'], 'priority' => 9, 'accepted_args' => 1],
+ $mocked_actions
+ );
+ $this->assertContains(
+ ['hook' => 'wp_enqueue_scripts', 'callback' => [Verify::class, 'get_success_token'], 'priority' => 10, 'accepted_args' => 1],
+ $mocked_actions
+ );
+ $this->assertContains(
+ ['hook' => 'admin_head', 'callback' => [$this->field, 'custom_admin_field_icon_style'], 'priority' => 10, 'accepted_args' => 1],
+ $mocked_actions
+ );
+ $this->assertContains(
+ ['hook' => 'admin_init', 'callback' => [$this->field, 'update_adcaptcha_label'], 'priority' => 10, 'accepted_args' => 1],
+ $mocked_actions
+ );
+ $this->assertContains(
+ ['hook' => 'admin_footer', 'callback' => [$this->field, 'enqueue_admin_script'], 'priority' => 10, 'accepted_args' => 1],
+ $mocked_actions
+ );
+ $this->assertContains(
+ ['hook' => 'gform_preview_body_open', 'callback' => [$this->field, 'enqueue_preview_scripts'], 'priority' => 10, 'accepted_args' => 1],
+ $mocked_actions
+ );
+ $this->assertContains(
+ ['hook' => 'gform_field_groups_form_editor', 'callback' => [$this->field, 'add_to_field_groups'], 'priority' => 10, 'accepted_args' => 1],
+ $mocked_filters
+ );
+ $this->assertContains(
+ ['hook' => 'gform_field_content', 'callback' => [$this->field, 'modify_gform_field_content'], 'priority' => 10, 'accepted_args' => 2],
+ $mocked_filters
+ );
+ $this->assertContains(
+ ['hook' => 'gform_validation', 'callback' => [$this->field, 'verify_captcha'], 'priority' => 10, 'accepted_args' => 1],
+ $mocked_filters
+ );
+ $this->assertContains(
+ ['hook' => 'gform_pre_render', 'callback' => [$this->field, 'handle_adcaptcha_token'], 'priority' => 10, 'accepted_args' => 1],
+ $mocked_filters
+ );
+ }
+
+ // Tests if 'add_to_field_groups' correctly adds the adCAPTCHA field to advanced fields and avoids duplication.
+ public function testAddToFieldGroups() {
+ $this->assertTrue(method_exists($this->field, 'add_to_field_groups'), 'Method add_to_field_groups does not exist');
+ $field_groups = [
+ 'advanced_fields' => [
+ 'fields' => [
+ ['data-type' => 'text'],
+ ['data-type' => 'email'],
+ ]
+ ]
+ ];
+ $field_groups_adcaptcha = [
+ 'advanced_fields' => [
+ 'fields' => [
+ ['data-type' => 'adcaptcha'],
+ ]
+ ]
+ ];
+ $result = $this->field->add_to_field_groups($field_groups);
+ $this->assertContains(
+ ['data-type' => 'adcaptcha', 'value' => $this->field->get_form_editor_field_title(), 'label' => $this->field->get_form_editor_field_title()],
+ $result['advanced_fields']['fields']
+ );
+ $fieldWithAdcaptcha = $this->field->add_to_field_groups($field_groups_adcaptcha);
+ $this->assertEquals($field_groups_adcaptcha, $fieldWithAdcaptcha, 'Expected the field groups to remain unchanged if adcaptcha field is already present');
+ }
+
+ // Tests if 'modify_gform_field_content' removes the label from adCAPTCHA fields while keeping other content unchanged.
+ public function testModifyGformFieldContent() {
+ $this->assertTrue(method_exists($this->field, 'modify_gform_field_content'), 'Method modify_gform_field_content does not exist');
+ $field = new MockGF_Field('adcaptcha');
+
+ $content = '';
+ $expectedModifiedContent = '';
+
+ $modifiedContent = $this->field->modify_gform_field_content($content, $field);
+ $this->assertEquals($expectedModifiedContent, $modifiedContent, 'Expected label to be removed for adcaptcha fields');
+ }
+
+ // Tests if 'verify_captcha' fails validation when the CAPTCHA token is empty.
+ public function testVerifyCaptchaFailsWhenTokenIsEmpty() {
+ Functions\when('wp_unslash')->justReturn('');
+ $validation_result = [
+ 'form' => ['fields' => [(object) ['type' => 'adcaptcha']]],
+ 'is_valid' => true
+ ];
+ $result = $this->field->verify_captcha($validation_result);
+ $this->assertFalse($result['is_valid']);
+ $this->assertTrue($result['form']['fields'][0]->failed_validation);
+ $this->assertEquals('Incomplete CAPTCHA, Please try again.', $result['form']['fields'][0]->validation_message);
+ }
+
+ // Tests if 'verify_captcha' fails validation when token verification fails.
+ public function testVerifyCaptchaFailsWhenTokenVerificationFails() {
+ Functions\when('wp_unslash')->justReturn('invalid_token');
+ $this->verifyMock->method('verify_token')->willReturn(false);
+ $validation_result = [
+ 'form' => ['fields' => [(object) ['type' => 'adcaptcha']]],
+ 'is_valid' => true
+ ];
+ $result = $this->field->verify_captcha($validation_result);
+ $this->assertFalse($result['is_valid']);
+ $this->assertTrue($result['form']['fields'][0]->failed_validation);
+ $this->assertEquals('Invalid token.', $result['form']['fields'][0]->validation_message);
+ }
+
+ // Tests if 'verify_captcha' passes validation when a valid token is provided.
+ public function testVerifyCaptchaSucceedsWhenTokenIsValid() {
+ Functions\when('wp_unslash')->justReturn('valid_token');
+ $this->verifyMock->method('verify_token')->willReturn(true);
+ $validation_result = [
+ 'form' => [
+ 'fields' => [
+ (object) [
+ 'type' => 'adcaptcha',
+ 'failed_validation' => false,
+ 'validation_message' => ''
+ ]
+ ]
+ ],
+ 'is_valid' => true
+ ];
+ $result = $this->field->verify_captcha($validation_result);
+ $this->assertTrue($result['is_valid']);
+ $this->assertFalse($result['form']['fields'][0]->failed_validation);
+ $this->assertEmpty($result['form']['fields'][0]->validation_message);
+ }
+
+ // Tests if 'custom_admin_field_icon_style' outputs the expected CSS styles.
+ public function testCustomAdminFieldIconStyle() {
+ $this->assertTrue(method_exists($this->field, 'custom_admin_field_icon_style'), 'Method custom_admin_field_icon_style does not exist');
+ ob_start();
+ $this->field->custom_admin_field_icon_style();
+ $capturedOutput = ob_get_clean();
+ $this->assertStringContainsString("#sidebar_field_info #sidebar_field_icon img {
+ width: 16px !important;
+ }", $capturedOutput, 'Expected the style to be output');
+ }
+
+ // Tests that 'update_adcaptcha_label' returns null when there are no forms to update.
+ public function testUpdateAdcaptchaLabelFormEmpty() {
+ $this->assertTrue(method_exists($this->field, 'update_adcaptcha_label'), 'Method update_adcaptcha_label does not exist');
+ $this->mockGFAPI->shouldReceive('get_forms')
+ ->once()
+ ->andReturn([]);
+ $result = $this->field->update_adcaptcha_label();
+ $this->assertNull($result, 'Expected null return value from update_adcaptcha_label');
+ }
+
+ // Tests that 'enqueue_admin_script' outputs the expected JavaScript for limiting adCAPTCHA field additions.
+ public function testEnqueueAdminScript() {
+ $this->assertTrue(method_exists($this->field, 'enqueue_admin_script'), 'Method enqueue_admin_script does not exist');
+ ob_start();
+ $this->field->enqueue_admin_script();
+ $capturedOutput = ob_get_clean();
+ $this->assertStringContainsString("if (typeof window.CanFieldBeAdded !== 'function') {
+ return;
+ }", $capturedOutput, 'Expected the script to be output');
+ $this->assertStringContainsString("let originalFunction = window.CanFieldBeAdded;", $capturedOutput, 'Expected the script to be output');
+ $this->assertStringContainsString('window.CanFieldBeAdded = function(type) {
+ if (type === "adcaptcha") {
+ if (GetFieldsByType(["adcaptcha"]).length > 0) {
+ gform.instances.dialogAlert("Field Error", "Only one adCAPTCHA field can be added.");
+ return false;
+ }
+ }
+ return originalFunction(type);
+ };', $capturedOutput, 'Expected the script to be output');
+ }
+
+ // Tests that 'handle_adcaptcha_token' correctly returns the form and outputs a script when a success token is present.
+ public function testHandleAdcaptchaToken() {
+ $this->assertTrue(method_exists($this->field, 'handle_adcaptcha_token'), 'Method handle_adcaptcha_token does not exist');
+
+ $_POST = [];
+ $form = ['id' => 1];
+ ob_start();
+ $result = $this->field->handle_adcaptcha_token($form);
+ $capturedOutput = ob_get_clean();
+ $this->assertEquals($form, $result, 'Expected the form to be returned unchanged');
+ $this->assertEmpty($capturedOutput, 'Expected no script output when success token is missing');
+
+ $_POST['adcaptcha_successToken'] = 'mocked-success-token';
+ ob_start();
+ $this->field->handle_adcaptcha_token($form);
+ $capturedOutput = ob_get_clean();
+ $this->assertStringContainsString("document.addEventListener('DOMContentLoaded', function()", $capturedOutput, 'Expected the script to be output');
+ $this->assertStringContainsString("let adCaptchaField = document.querySelector('.adcaptcha_successToken');", $capturedOutput, 'Expected the script to be output');
+ $this->assertStringContainsString("if (adCaptchaField) {
+ setTimeout(function() {
+ if (window.adcap) {
+ window.adcap.setVerificationState('success');
+ adCaptchaField.value = 'mocked-success-token';
+ }
+ }, 500);
+ }", $capturedOutput, 'Expected the script to be output');
+ }
+
+ // Tests that 'enqueue_preview_scripts' outputs the expected JavaScript for rendering adCAPTCHA in preview mode.
+ public function testEnqueuePreviewScripts() {
+ $this->assertTrue(method_exists($this->field, 'enqueue_preview_scripts'), 'Method enqueue_preview_scripts does not exist');
+ $formID = 1;
+ ob_start();
+ $this->field->enqueue_preview_scripts($formID);
+ $capturedOutput = ob_get_clean();
+ $this->assertStringContainsString("document.addEventListener('DOMContentLoaded', function()", $capturedOutput, 'The enqueue_preview_scripts method was not called correctly.');
+ $this->assertStringContainsString("let captchaContainer = document.querySelector('.ginput_container_adcaptcha');", $capturedOutput, 'The enqueue_preview_scripts method was not called correctly.');
+ $this->assertStringContainsString("if (captchaContainer) {
+ let messageDiv = document.createElement('div');
+ messageDiv.className = 'ginput_container adcaptcha-message';
+ messageDiv.innerText = 'adCAPTCHA will be rendered here.';
+ captchaContainer.prepend(messageDiv);
+ }", $capturedOutput, 'The enqueue_preview_scripts method was not called correctly.');
+ }
+
+ // Tests that 'get_field_input' returns the correct HTML output based on form ID and editor mode.
+ public function testGetFieldInput() {
+ $this->assertTrue(method_exists($this->field, 'get_field_input'), 'Method get_field_input does not exist');
+ $form = ['id' => null];
+ $result = $this->field->get_field_input($form, '', null);
+ $this->assertEmpty($result, 'Expected an empty string when form ID is missing');
+ $form = ['id' => 1];
+ $result = $this->field->get_field_input($form, '', null);
+ $this->assertEquals("
adCAPTCHA will be rendered here.
", $result, 'Expected the adCAPTCHA message to be rendered');
+
+ $form_id = 1;
+ $field_id = 123;
+ $this->field->isFormEditor = false;
+ $result = $this->field->get_field_input($form, '', null);
+ $this->assertStringContainsString("document.addEventListener('DOMContentLoaded', function() {
+ var hiddenToken = document.querySelector('.adcaptcha_successToken');
+ if (hiddenToken) {
+ hiddenToken.id = 'input_{$form_id}_{$field_id}';
+ }
+ });", $result, 'Expected CAPTCHA HTML to be rendered when is_form_editor is false');
+ }
+
+ // Tests that 'get_form_editor_field_title' returns the expected title with the correct text domain.
+ public function testGetFormEditorFieldTitle() {
+ $this->assertTrue(
+ method_exists($this->field, 'get_form_editor_field_title'),
+ 'Method get_form_editor_field_title does not exist'
+ );
+ $expectedText = 'adCAPTCHA';
+ $expectedDomain = 'adcaptcha';
+ $result = $this->field->get_form_editor_field_title();
+ $this->assertEquals(
+ "[{$expectedDomain}] {$expectedText}",
+ $result,
+ 'Expected the title to match the expected text with correct text domain'
+ );
+ }
+
+ // Tests that 'get_form_editor_field_settings' returns an array with the expected field settings.
+ public function testGetFormEditorFieldSettings() {
+ $this->assertTrue(method_exists($this->field, 'get_form_editor_field_settings'), 'Method get_form_editor_field_settings does not exist');
+ $data = [ 'description_setting', 'error_message_setting', 'label_placement_setting', 'css_class_setting',];
+ $result = $this->field->get_form_editor_field_settings();
+ $this->assertIsArray($result, 'Expected an array to be returned');
+ $this->assertEquals($data, $result, 'Expected the array keys to match the expected data');
+ }
+
+ // Tests that 'get_form_editor_field_description' returns the expected description with the correct text domain.
+ public function testGetFormEditorFieldDescription() {
+ $this->assertTrue(
+ method_exists($this->field, 'get_form_editor_field_description'),
+ 'Method get_form_editor_field_description does not exist'
+ );
+ $expectedText = 'Adds an adCAPTCHA verification field to enhance security and prevent spam submissions on your forms.';
+ $expectedDomain = 'adcaptcha-for-forms';
+ $result = $this->field->get_form_editor_field_description();
+ $this->assertEquals(
+ "[{$expectedDomain}] {$expectedText}",
+ $result,
+ 'Expected the description to match the expected text with correct text domain'
+ );
+ }
+
+ // Tests that 'get_form_editor_field_icon' returns the correct icon URL.
+ public function testGetFromEditorFieldIcon() {
+ $this->assertTrue(method_exists($this->field, 'get_form_editor_field_icon'), 'Method get_form_editor_field_icon does not exist');
+ $icon_url = $this->field->get_form_editor_field_icon();
+ $this->assertStringContainsString(
+ '../../../assets/adcaptcha_icon.png',
+ $icon_url,
+ 'Expected the icon URL to be correctly formed'
+ );
+ }
+ }
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
index 8935796..3bb7f7e 100644
--- a/tests/bootstrap.php
+++ b/tests/bootstrap.php
@@ -1,4 +1,5 @@
errors);
+ $code = array_key_first($this->errors);
}
- return isset($this->errors[$code]) ? $this->errors[$code][0] : null;
+
+ return isset($this->errors[$code]) ? $this->errors[$code][0] : '';
}
-
public function get_error_data($code = '') {
return isset($this->error_data[$code]) ? $this->error_data[$code] : null;
}
diff --git a/tests/test_helpers.php b/tests/test_helpers.php
index 49c2b75..1c4a93f 100644
--- a/tests/test_helpers.php
+++ b/tests/test_helpers.php
@@ -47,4 +47,4 @@ function check_hook_registration($mocked_hooks, $hook_name, $priority = 10, $acc
}
}
return false;
-}
\ No newline at end of file
+}