diff --git a/.eslintrc b/.eslintrc
index 0ea0308..38a9ec2 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -5,31 +5,25 @@
"plugin:cypress/recommended"
],
"parser": "@typescript-eslint/parser",
- "plugins": [
- "@typescript-eslint"
- ],
+ "plugins": ["@typescript-eslint"],
"rules": {
"prettier/prettier": "warn",
"import/no-unresolved": 0,
"@typescript-eslint/ban-ts-comment": "off",
"camelcase": "off"
},
- "overrides":
- [{
- "files": ["*.jsx"],
- "rules": {
- "@typescript-eslint/explicit-module-boundary-types": ["off"]
- }
- }],
+ "overrides": [
+ {
+ "files": ["*.jsx"],
+ "rules": {
+ "@typescript-eslint/explicit-module-boundary-types": ["off"]
+ }
+ }
+ ],
"settings": {
"import/resolver": {
"alias": {
- "map": [
- [
- "src",
- "./src"
- ],
- ]
+ "map": [["src", "./src"]]
}
}
}
diff --git a/.gitignore b/.gitignore
index d3d035a..3064e04 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@ node_modules
build
vendor
yarn-error.log
+.phpunit.result.cache
diff --git a/composer.json b/composer.json
index b6d97f8..03a4c59 100644
--- a/composer.json
+++ b/composer.json
@@ -28,6 +28,6 @@
"scripts": {
"lint:php": "phpcs .",
"lint:php-fix": "phpcbf .",
- "test:php": "phpunit --dont-report-useless-tests --configuration ./phpunit.xml.dist"
+ "test:php": "phpunit --dont-report-useless-tests --configuration ./phpunit.xml --testdox"
}
}
diff --git a/helper/Helper.php b/helper/Helper.php
index c1f1a58..7e8415d 100644
--- a/helper/Helper.php
+++ b/helper/Helper.php
@@ -18,13 +18,6 @@
*/
class Helper {
- /**
- * Name of flag environment.
- *
- * @var string $env_option_name
- */
- public static $env_option_name = 'mr_feature_flags_env';
-
/**
* Flag search helper.
*
diff --git a/includes/Api/FlagOptions.php b/includes/Api/FlagOptions.php
deleted file mode 100644
index 86641e1..0000000
--- a/includes/Api/FlagOptions.php
+++ /dev/null
@@ -1,156 +0,0 @@
- \WP_REST_SERVER::READABLE,
- 'callback' => [ $this, 'get_all_flags' ],
- 'permission_callback' => '__return_true',
- ],
- [
- 'methods' => \WP_REST_SERVER::EDITABLE,
- 'callback' => [ $this, 'post_flags' ],
- 'permission_callback' => '__return_true',
- ],
- ]
- );
-
- register_rest_route(
- 'feature-flags/v1',
- 'flags/env',
- [
- [
- 'methods' => \WP_REST_SERVER::READABLE,
- 'callback' => [ $this, 'get_flag_env' ],
- 'permission_callback' => '__return_true',
- ],
- ]
- );
-
- }
- );
- }
-
- /**
- * Get all flags from options
- *
- * @return mixed List of flags.
- */
- public function get_all_flags() {
- $flags = get_option( self::$option_name );
-
- if ( empty( $flags ) ) {
- return rest_ensure_response( [] );
- }
-
- return rest_ensure_response( $flags );
- }
-
- /**
- * Insert / Update flags in options table.
- *
- * @param WP_Request $request API request.
- *
- * @return mixed List of flags.
- */
- public function post_flags( $request ) {
- $flags = $request->get_json_params();
-
- if ( is_array( $flags ) ) {
- $result = update_option( self::$option_name, $flags );
- return rest_ensure_response(
- array(
- 'status' => 200,
- 'success' => true,
- ),
- );
-
- } else {
- return new \WP_Error( 'invalid_input', 'Cannot update flags', array( 'status' => 400 ) );
- }
- }
-
- /**
- * Get Feature Flag environment.
- *
- * @return mixed List of flags.
- */
- public function get_flag_env() {
- $env = get_option( self::$env_option_name );
-
- if ( empty( $env ) ) {
- return rest_ensure_response( [ 'env' => 'prod' ] );
- }
-
- return rest_ensure_response( $env );
- }
-
- /**
- * Register settings action method.
- *
- * @return void
- * @since 1.0.0
- */
- public function register_settings() {
-
- add_menu_page(
- 'Feature Flags',
- 'Feature Flags',
- 'manage_options',
- 'mr-feature-flags',
- [ $this, 'render_page' ],
- 'data:image/svg+xml;base64,' . base64_encode( ' ' )
- );
- }
-
- /**
- * Render page
- */
- public function render_page() {
- echo '
';
- }
-}
diff --git a/includes/Api/Flags.php b/includes/Api/Flags.php
new file mode 100644
index 0000000..ccfcc42
--- /dev/null
+++ b/includes/Api/Flags.php
@@ -0,0 +1,101 @@
+ \WP_REST_SERVER::READABLE,
+ 'callback' => [ $this, 'get_all_flags' ],
+ 'permission_callback' => function () {
+ return current_user_can( 'manage_options' );
+ },
+ ],
+ [
+ 'methods' => \WP_REST_SERVER::EDITABLE,
+ 'callback' => [ $this, 'post_flags' ],
+ 'permission_callback' => function () {
+ return current_user_can( 'manage_options' );
+ },
+ ],
+ ]
+ );
+ }
+ );
+ }
+
+ /**
+ * Get all flags from options
+ *
+ * @return mixed List of flags.
+ */
+ public function get_all_flags() {
+ $flags = get_option( self::$option_name );
+
+ if ( empty( $flags ) ) {
+ return rest_ensure_response( [] );
+ }
+
+ return rest_ensure_response( $flags );
+ }
+
+ /**
+ * Insert / Update flags in options table.
+ *
+ * @param WP_Request $request API request.
+ *
+ * @return mixed List of flags.
+ */
+ public function post_flags( $request ) {
+ $flags = $request->get_json_params();
+
+ if ( is_array( $flags ) ) {
+ $result = update_option( self::$option_name, $flags );
+ return rest_ensure_response(
+ array(
+ 'status' => 200,
+ 'success' => true,
+ ),
+ );
+
+ } else {
+ return new \WP_Error( 'invalid_input', 'Cannot update flags', array( 'status' => 400 ) );
+ }
+ }
+
+}
diff --git a/includes/Settings.php b/includes/Settings.php
index 9e057a5..f39360e 100644
--- a/includes/Settings.php
+++ b/includes/Settings.php
@@ -37,8 +37,8 @@ public function register_feature_settings() {
public function register_settings() {
add_menu_page(
- 'Feature Flags',
- 'Feature Flags',
+ __( 'Feature Flags', 'mr-feature-flags' ),
+ __( 'Feature Flags', 'mr-feature-flags' ),
'manage_options',
'mr-feature-flags',
[ $this, 'render_page' ],
diff --git a/includes/FeatureFlags.php b/includes/Utils.php
similarity index 88%
rename from includes/FeatureFlags.php
rename to includes/Utils.php
index b121689..a1bbd91 100644
--- a/includes/FeatureFlags.php
+++ b/includes/Utils.php
@@ -1,6 +1,6 @@
+
+
+
+ ./tests/Unit/
+
+
+
+
diff --git a/plugin.php b/plugin.php
index c557c75..8f7beb4 100644
--- a/plugin.php
+++ b/plugin.php
@@ -19,7 +19,7 @@
declare( strict_types = 1 );
namespace MR\FeatureFlags;
-use MR\FeatureFlags\Api\FlagOptions;
+use MR\FeatureFlags\Api\Flags;
// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
@@ -120,7 +120,7 @@ function(string $page): void {
true
);
- $feature_flag_meta = get_option( FeatureFlags::$option_name );
+ $feature_flag_meta = get_option( Utils::$option_name );
$flags_list = [];
if(is_array($feature_flag_meta)) {
$flags_list = $feature_flag_meta;
@@ -141,14 +141,14 @@ function(string $page): void {
$mr_feature_flags_admin_settings = new Settings();
$mr_feature_flags_admin_settings->register_feature_settings();
-$mr_feature_flags_register_api = new FlagOptions();
+$mr_feature_flags_register_api = new Flags();
$mr_feature_flags_register_api->register_flags_endpoints();
add_filter( 'plugin_action_links_mr-feature-flags/plugin.php', function ( $links )
{
- // Build and escape the URL.
+
$url = esc_url(
add_query_arg(
'page',
@@ -156,9 +156,9 @@ function(string $page): void {
get_admin_url() . 'admin.php'
)
);
- // Create the link.
+
$settings_link = "" . __( 'Settings', 'mr-feature-flags' ) . ' ';
- // Adds the link to the end of the array.
+
array_push(
$links,
$settings_link
@@ -166,6 +166,3 @@ function(string $page): void {
return $links;
}
);
-
-
-// update_option( 'mr_feature_flags', [ ["id" => 1, "name" => "login", "enabled" => false],["id" => 2, "name" => "Reg", "enabled" => false]] );
diff --git a/src/components/DeleteModal.tsx b/src/components/DeleteModal.tsx
index 0d17605..762c33a 100644
--- a/src/components/DeleteModal.tsx
+++ b/src/components/DeleteModal.tsx
@@ -1,20 +1,30 @@
import { Modal, Button } from '@wordpress/components';
+import { __ } from '@wordpress/i18n';
import { Flag } from '../../types';
interface DeleteModalProps {
closeModal: () => void;
item: Flag;
handleDeleteFlag: (id: number) => void;
}
+
const DeleteModal = ({
closeModal,
item,
handleDeleteFlag,
}: DeleteModalProps): JSX.Element => {
return (
-
+
- Are you sure want to delete flag "{item.name}
- "?
+ {
+ // eslint-disable-next-line @wordpress/i18n-no-variables
+ __(
+ `Are you sure want to delete flag "${item.name}" ?`,
+ 'mr-feature-flags'
+ )
+ }
handleDeleteFlag(item.id)}
style={{ marginRight: 10 }}
>
- Yes
+ {__('Yes', 'mr-feature-flags')}
- Cancel
+ {__('Cancel', 'mr-feature-flags')}
);
diff --git a/src/components/Header.tsx b/src/components/Header.tsx
index bf8208c..94ad280 100644
--- a/src/components/Header.tsx
+++ b/src/components/Header.tsx
@@ -1,18 +1,20 @@
import { Flex, FlexItem } from '@wordpress/components';
+import { __ } from '@wordpress/i18n';
+
export default function (): JSX.Element {
return (
- Flag Name
+ {__('Flag Name', 'mr-feature-flags')}
- Status
+ {__('Status', 'mr-feature-flags')}
-
- SDK Settings
+
+ {__('SDK Settings', 'mr-feature-flags')}
- Delete Flag
+ {__('Delete Flag', 'mr-feature-flags')}
);
diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx
index dea5988..7400645 100644
--- a/src/components/Layout.tsx
+++ b/src/components/Layout.tsx
@@ -5,6 +5,7 @@ import { Flag } from '../../types';
import SubmitControls from './SubmitControls';
import { getFlags } from '../utils';
import Header from './Header';
+import { __ } from '@wordpress/i18n';
const Layout = (): JSX.Element => {
const [flags, setFlags] = useState([]);
@@ -30,7 +31,7 @@ const Layout = (): JSX.Element => {
return (
<>
-
Feature Flags settings
+
{__('Feature Flags settings', 'mr-feature-flags')}
{lastFlag ?
: ''}
{isLoading ? (
diff --git a/src/components/LineItem.tsx b/src/components/LineItem.tsx
index f851220..c1ce29b 100644
--- a/src/components/LineItem.tsx
+++ b/src/components/LineItem.tsx
@@ -10,6 +10,7 @@ import { useState, useRef, useEffect } from '@wordpress/element';
import { Flag } from '../../types';
import DeleteModal from './DeleteModal';
import SdkModal from './SdkModal';
+import { __ } from '@wordpress/i18n';
interface LineItemProps {
flags: Flag[];
@@ -119,12 +120,15 @@ const LineItem = ({
- {'> SDK'}
+ {__('> SDK', 'mr-feature-flags')}
@@ -132,7 +136,7 @@ const LineItem = ({
icon={'trash'}
isDestructive
variant="tertiary"
- label="Delete Flag"
+ label={__('Delete Flag', 'mr-feature-flags')}
onClick={() => handleDeleteModal(item)}
/>
@@ -141,13 +145,19 @@ const LineItem = ({
<>
{}
{}
diff --git a/src/components/SdkModal.tsx b/src/components/SdkModal.tsx
index 0b64e82..ce16d96 100644
--- a/src/components/SdkModal.tsx
+++ b/src/components/SdkModal.tsx
@@ -3,6 +3,7 @@ import Snippet from './Snippet';
import { useMemo, useState, useEffect } from '@wordpress/element';
import { useCopyToClipboard } from '@wordpress/compose';
import { Flag } from '../../types';
+import { __ } from '@wordpress/i18n';
interface SdkModalProps {
item: Flag;
@@ -48,7 +49,7 @@ domReady(function () {
}, [item.name]);
const phpSnippet = useMemo(() => {
- return `if ( MR\\FeatureFlags\\FeatureFlags::is_enabled( '${item.name}' ) ) {
+ return `if ( MR\\FeatureFlags\\Utils::is_enabled( '${item.name}' ) ) {
// php code goes here...
}`;
}, [item.name]);
@@ -68,7 +69,7 @@ domReady(function () {
return (
-
PHP Snippet
+ {__('PHP Snippet', 'mr-feature-flags')}
-
JS Snippet
+ {__('JavaScript Snippet', 'mr-feature-flags')}
- Add Flag
+ {__('Add Flag', 'mr-feature-flags')}
@@ -72,7 +73,9 @@ const SubmitControls = ({
onClick={handleSave}
disabled={disableSave || isSaving}
>
- {isSaving ? 'Saving' : 'Save'}
+ {isSaving
+ ? __('Saving', 'mr-feature-flags')
+ : __('Save', 'mr-feature-flags')}
@@ -80,7 +83,7 @@ const SubmitControls = ({
variant="tertiary"
onClick={() => location.reload()}
>
- Cancel
+ {__('Cancel', 'mr-feature-flags')}
diff --git a/src/components/__tests__/DeleteModal.test.js b/src/components/__tests__/DeleteModal.test.js
index 5a37d88..6513ddf 100644
--- a/src/components/__tests__/DeleteModal.test.js
+++ b/src/components/__tests__/DeleteModal.test.js
@@ -17,7 +17,7 @@ describe('DeleteModal component', () => {
render( );
const modalText = screen.getByText(
- `Are you sure want to delete flag "${flag.name}"?`
+ `Are you sure want to delete flag "${flag.name}" ?`
);
expect(modalText).toBeInTheDocument();
});
diff --git a/tests/Unit/FlagsTest.php b/tests/Unit/FlagsTest.php
new file mode 100644
index 0000000..1dd0db0
--- /dev/null
+++ b/tests/Unit/FlagsTest.php
@@ -0,0 +1,99 @@
+1, 'name'=>'Test','enabled'=>true]];
+
+ \Brain\Monkey\Functions\when('get_option')->justReturn($mock_option_value);
+ \Brain\Monkey\Functions\when('rest_ensure_response')->returnArg();
+
+ $flags = new Flags();
+ $result = $flags->get_all_flags();
+ $this->assertEquals($result, $mock_option_value);
+ }
+
+ public function test_get_all_flags_method_should_return_empty_array_if_value_is_not_set() {
+ $mock_option_value = '';
+
+ \Brain\Monkey\Functions\when('get_option')->justReturn($mock_option_value);
+ \Brain\Monkey\Functions\when('rest_ensure_response')->returnArg();
+
+ $flags = new Flags();
+ $result = $flags->get_all_flags();
+ $this->assertEquals($result, []);
+ }
+
+ public function test_get_all_flags_method_should_return_multiple_flags_from_options_table() {
+ $mock_option_value = [['id'=>1, 'name'=>'Test','enabled'=>true],['id'=>2, 'name'=>'Test2','enabled'=>false]];
+
+ \Brain\Monkey\Functions\when('get_option')->justReturn($mock_option_value);
+ \Brain\Monkey\Functions\when('rest_ensure_response')->returnArg();
+
+ $flags = new Flags();
+ $result = $flags->get_all_flags();
+ $this->assertEquals($result, $mock_option_value);
+ }
+
+ public function test_post_flags_methods_should_return_success_if_input_is_array() {
+
+ $request_mock = \Mockery::mock('WP_Request');
+ $request_mock->shouldReceive('get_json_params')->andReturn(['param1' => 'value1']);
+
+ \Brain\Monkey\Functions\when('update_option')->justReturn(true);
+ \Brain\Monkey\Functions\when('rest_ensure_response')->returnArg();
+
+ global $wp;
+ $wp = new \stdClass();
+ $wp->request = $request_mock;
+
+ $flags = new Flags();
+ $result = $flags->post_flags($request_mock);
+
+ $this->assertEquals(['status'=>200, 'success' => true], $result);
+
+ unset($GLOBALS['wp']);
+ }
+
+ public function test_post_flags_methods_should_throw_error_if_input_is_not_an_array() {
+
+ $request_mock = \Mockery::mock('WP_Request');
+ $request_mock->shouldReceive('get_json_params')->andReturn('test');
+
+ global $wp;
+ $wp = new \stdClass();
+ $wp->request = $request_mock;
+
+ $error_mock = \Mockery::mock('WP_Error');
+
+ \Brain\Monkey\Functions\expect('post_flags')->andReturn($error_mock);
+
+
+ $flags = new Flags();
+ $result = $flags->post_flags($request_mock);
+
+ $this->assertInstanceOf('WP_Error', $result);
+
+ }
+
+
+}
diff --git a/tests/Unit/HelperTest.php b/tests/Unit/HelperTest.php
new file mode 100644
index 0000000..e72f74a
--- /dev/null
+++ b/tests/Unit/HelperTest.php
@@ -0,0 +1,41 @@
+1, 'name'=>'Test','enabled'=>true]],'name','Test');
+ $this->assertTrue($result);
+ }
+
+ public function test_search_flag_method_should_return_false_if_flags_are_empty() {
+ $result = Helper::search_flag([],'name','Test');
+ $this->assertFalse($result);
+ }
+
+ public function test_search_flag_method_should_return_false_if_flag_name_present_but_disabled() {
+ $result = Helper::search_flag([],'name','Test');
+ $this->assertFalse($result);
+ }
+
+ public function test_search_flag_method_should_return_false_if_flag_not_present() {
+ $result = Helper::search_flag([['id'=>1, 'name'=>'Test','enabled'=>true]],'name','Test1');
+ $this->assertFalse($result);
+ }
+}
diff --git a/tests/Unit/UtilsTest.php b/tests/Unit/UtilsTest.php
new file mode 100644
index 0000000..5a193f9
--- /dev/null
+++ b/tests/Unit/UtilsTest.php
@@ -0,0 +1,62 @@
+1, 'name'=>'Test','enabled'=>true]];
+
+ \Brain\Monkey\Functions\when('get_option')->justReturn($mock_option_value);
+
+ $result = Utils::is_enabled('Test');
+ $this->assertTrue($result);
+ }
+
+ public function test_is_enabled_method_should_return_false_if_no_flags_exist() {
+ $mock_option_value = '';
+
+ \Brain\Monkey\Functions\when('get_option')->justReturn($mock_option_value);
+
+ $result = Utils::is_enabled('Test');
+ $this->assertFalse($result);
+ }
+
+ public function test_is_enabled_method_should_return_false_if_flag_name_present_and_disabled() {
+ $mock_option_value = [['id'=>1, 'name'=>'Test','enabled'=>false]];
+
+ \Brain\Monkey\Functions\when('get_option')->justReturn($mock_option_value);
+
+ $result = Utils::is_enabled('Test');
+ $this->assertFalse($result);
+ }
+
+ public function test_is_enabled_method_should_return_false_if_flag_name_nor_present() {
+ $mock_option_value = [['id'=>1, 'name'=>'Test','enabled'=>false]];
+
+ \Brain\Monkey\Functions\when('get_option')->justReturn($mock_option_value);
+
+ $result = Utils::is_enabled('Test1');
+ $this->assertFalse($result);
+ }
+
+
+}
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
new file mode 100644
index 0000000..a04dd1b
--- /dev/null
+++ b/tests/bootstrap.php
@@ -0,0 +1,6 @@
+