* Add Task model API

* Add task dismiss and snooze methods

* Add tests

* Fix snooze time check
This commit is contained in:
Joshua T Flowers 2021-09-20 17:17:22 -04:00 committed by GitHub
parent 2be141bd16
commit 9a361dde6c
3 changed files with 551 additions and 2 deletions

View File

@ -0,0 +1,296 @@
<?php
/**
* Handles task related methods.
*/
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks;
/**
* Task class.
*/
class Task {
/**
* ID.
*
* @var string
*/
public $id = '';
/**
* Title.
*
* @var string
*/
public $title = '';
/**
* Title.
*
* @var string
*/
public $content = '';
/**
* Action label.
*
* @var string
*/
public $action_label = '';
/**
* Action URL.
*
* @var string|null
*/
public $action_url = null;
/**
* Task completion.
*
* @var bool
*/
protected $is_complete = false;
/**
* Viewing capability.
*
* @var bool
*/
protected $can_view = true;
/**
* Time string.
*
* @var string|null
*/
public $time = null;
/**
* Dismissability.
*
* @var bool
*/
protected $is_dismissable = false;
/**
* Snoozeability.
*
* @var bool
*/
protected $is_snoozeable = false;
/**
* Snoozeability.
*
* @var string|null
*/
public $snoozed_until = null;
/**
* Name of the dismiss option.
*
* @var string
*/
const DISMISSED_OPTION = 'woocommerce_task_list_dismissed_tasks';
/**
* Name of the snooze option.
*
* @var string
*/
const SNOOZED_OPTION = 'woocommerce_task_list_remind_me_later_tasks';
/**
* Duration to milisecond mapping.
*
* @var string
*/
protected $duration_to_ms = array(
'day' => DAY_IN_SECONDS * 1000,
'hour' => HOUR_IN_SECONDS * 1000,
'week' => WEEK_IN_SECONDS * 1000,
);
/**
* Constructor
*
* @param array $data Task list data.
*/
public function __construct( $data = array() ) {
$defaults = array(
'id' => null,
'title' => '',
'content' => '',
'action_label' => __( "Let's go", 'woocommerce-admin' ),
'action_url' => null,
'is_complete' => false,
'can_view' => true,
'time' => null,
'is_dismissable' => false,
'is_snoozeable' => false,
'snoozed_until' => null,
);
$data = wp_parse_args( $data, $defaults );
$this->id = (string) $data['id'];
$this->title = (string) $data['title'];
$this->content = (string) $data['content'];
$this->action_label = (string) $data['action_label'];
$this->action_url = (string) $data['action_url'];
$this->is_complete = (bool) $data['is_complete'];
$this->can_view = (bool) $data['can_view'];
$this->time = (string) $data['time'];
$this->is_dismissable = (bool) $data['is_dismissable'];
$this->is_snoozeable = (bool) $data['is_snoozeable'];
$snoozed_tasks = get_option( self::SNOOZED_OPTION, array() );
if ( isset( $snoozed_tasks[ $this->id ] ) ) {
$this->snoozed_until = $snoozed_tasks[ $this->id ];
}
}
/**
* Bool for task dismissal.
*
* @return bool
*/
public function is_dismissed() {
if ( ! $this->is_dismissable ) {
return false;
}
$dismissed = get_option( self::DISMISSED_OPTION, array() );
return in_array( $this->id, $dismissed, true );
}
/**
* Dismiss the task.
*
* @return bool
*/
public function dismiss() {
if ( ! $this->is_dismissable ) {
return false;
}
$dismissed = get_option( self::DISMISSED_OPTION, array() );
$dismissed[] = $this->id;
$update = update_option( self::DISMISSED_OPTION, array_unique( $dismissed ) );
if ( $update ) {
wc_admin_record_tracks_event( 'tasklist_dismiss_task', array( 'task_name' => $this->id ) );
}
return $update;
}
/**
* Undo task dismissal.
*
* @return bool
*/
public function undo_dismiss() {
$dismissed = get_option( self::DISMISSED_OPTION, array() );
$dismissed = array_diff( $dismissed, array( $this->id ) );
$update = update_option( self::DISMISSED_OPTION, $dismissed );
if ( $update ) {
wc_admin_record_tracks_event( 'tasklist_undo_dismiss_task', array( 'task_name' => $this->id ) );
}
return $update;
}
/**
* Bool for task snoozed.
*
* @return bool
*/
public function is_snoozed() {
if ( ! $this->is_snoozeable ) {
return false;
}
$snoozed = get_option( self::SNOOZED_OPTION, array() );
return isset( $snoozed[ $this->id ] ) && $snoozed[ $this->id ] > ( time() * 1000 );
}
/**
* Snooze the task.
*
* @param string $duration Duration to snooze. day|hour|week.
* @return bool
*/
public function snooze( $duration = 'day' ) {
if ( ! $this->is_snoozeable ) {
return false;
}
$snoozed = get_option( self::SNOOZED_OPTION, array() );
$snoozed_until = $this->duration_to_ms[ $duration ] + ( time() * 1000 );
$snoozed[ $this->id ] = $snoozed_until;
$update = update_option( self::SNOOZED_OPTION, $snoozed );
if ( $update ) {
if ( $update ) {
wc_admin_record_tracks_event( 'tasklist_remindmelater_task', array( 'task_name' => $this->id ) );
$this->snoozed_until = $snoozed_until;
}
}
return $update;
}
/**
* Undo task snooze.
*
* @return bool
*/
public function undo_snooze() {
$snoozed = get_option( self::SNOOZED_OPTION, array() );
unset( $snoozed[ $this->id ] );
$update = update_option( self::SNOOZED_OPTION, $snoozed );
if ( $update ) {
wc_admin_record_tracks_event( 'tasklist_undo_remindmelater_task', array( 'task_name' => $this->id ) );
}
return $update;
}
/**
* Bool for task visibility.
*
* @return bool
*/
public function is_visible() {
return $this->can_view && ! $this->is_snoozed() && ! $this->is_dismissed();
}
/**
* Get the task as JSON.
*
* @return array
*/
public function get_json() {
return array(
'id' => $this->id,
'title' => $this->title,
'content' => $this->content,
'actionLabel' => $this->action_label,
'actionUrl' => $this->action_url,
'isComplete' => $this->is_complete,
'isVisible' => $this->is_visible(),
'time' => $this->time,
'isDismissed' => $this->is_dismissed(),
'isDismissable' => $this->is_dismissable,
'isSnoozed' => $this->is_snoozed(),
'isSnoozeable' => $this->is_snoozeable,
'snoozedUntil' => $this->snoozed_until,
);
}
}

View File

@ -5,6 +5,8 @@
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks; namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks;
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
/** /**
* Task List class. * Task List class.
*/ */
@ -107,7 +109,7 @@ class TaskList {
* @param array $args Task properties. * @param array $args Task properties.
*/ */
public function add_task( $args ) { public function add_task( $args ) {
$this->tasks[] = $args; $this->tasks[] = new Task( $args );
} }
/** /**
@ -121,7 +123,12 @@ class TaskList {
'title' => $this->title, 'title' => $this->title,
'isHidden' => $this->is_hidden(), 'isHidden' => $this->is_hidden(),
'isComplete' => $this->is_complete(), 'isComplete' => $this->is_complete(),
'tasks' => $this->tasks, 'tasks' => array_map(
function( $task ) {
return $task->get_json();
},
$this->tasks
),
); );
} }

View File

@ -0,0 +1,246 @@
<?php
/**
* Test the Task class.
*
* @package WooCommerce\Admin\Tests\OnboardingTasks
*/
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
/**
* class WC_Tests_OnboardingTasks_Task
*/
class WC_Tests_OnboardingTasks_Task extends WC_Unit_Test_Case {
/**
* Task.
*
* @var Task|null
*/
protected $task = null;
/**
* Tests that a task is visible by default.
*/
public function test_capability_visible() {
$task = new Task(
array(
'id' => 'wc-unit-test-task',
)
);
$this->assertEquals( true, $task->is_visible() );
}
/**
* Tests that a task is not visible when not capable of being viewed.
*/
public function test_capability_not_visible() {
$task = new Task(
array(
'id' => 'wc-unit-test-task',
'can_view' => false,
)
);
$this->assertEquals( false, $task->is_visible() );
}
/**
* Tests that a task can be dismissed.
*/
public function test_dismiss() {
$task = new Task(
array(
'id' => 'wc-unit-test-task',
'is_dismissable' => true,
)
);
$update = $task->dismiss();
$dismissed = get_option( Task::DISMISSED_OPTION, array() );
$this->assertEquals( true, $update );
$this->assertContains( $task->id, $dismissed );
}
/**
* Tests that a dismissal can be undone.
*/
public function test_undo_dismiss() {
$task = new Task(
array(
'id' => 'wc-unit-test-task',
'is_dismissable' => true,
)
);
$task->dismiss();
$task->undo_dismiss();
$dismissed = get_option( Task::DISMISSED_OPTION, array() );
$this->assertNotContains( $task->id, $dismissed );
}
/**
* Tests that a non-dismissable task cannot be dismissed.
*/
public function test_not_dismissable() {
$task = new Task(
array(
'id' => 'wc-unit-test-non-dismissable-task',
'is_dismissable' => false,
)
);
$update = $task->dismiss();
$dismissed = get_option( Task::DISMISSED_OPTION, array() );
$this->assertEquals( false, $update );
$this->assertNotContains( $task->id, $dismissed );
}
/**
* Tests that a dismissed task is not visible.
*/
public function test_dismissed_visibility() {
$task = new Task(
array(
'id' => 'wc-unit-test-task',
'is_dismissable' => true,
)
);
$task->dismiss();
$this->assertEquals( false, $task->is_visible() );
}
/**
* Tests that a task can be snoozed.
*/
public function test_snooze() {
$task = new Task(
array(
'id' => 'wc-unit-test-snoozeable-task',
'is_snoozeable' => true,
)
);
$update = $task->snooze();
$snoozed = get_option( Task::SNOOZED_OPTION, array() );
$this->assertEquals( true, $update );
$this->assertArrayHasKey( $task->id, $snoozed );
}
/**
* Tests that a task can be unsnoozed.
*/
public function test_undo_snooze() {
$task = new Task(
array(
'id' => 'wc-unit-test-snoozeable-task',
'is_snoozeable' => true,
)
);
$task->snooze();
$task->undo_snooze();
$snoozed = get_option( Task::SNOOZED_OPTION, array() );
$this->assertArrayNotHasKey( $task->id, $snoozed );
}
/**
* Tests that a task is not visible when snoozed.
*/
public function test_snoozed_visibility() {
$task = new Task(
array(
'id' => 'wc-unit-test-snoozeable-task',
'is_snoozeable' => true,
)
);
$task->snooze();
$this->assertEquals( false, $task->is_visible() );
}
/**
* Tests that a task's snooze time is automatically added.
*/
public function test_snoozed_until() {
$time = time() * 1000;
$snoozed = get_option( Task::SNOOZED_OPTION, array() );
$snoozed['wc-unit-test-task'] = $time;
update_option( Task::SNOOZED_OPTION, $snoozed );
$task = new Task(
array(
'id' => 'wc-unit-test-task',
'is_snoozeable' => true,
)
);
$this->assertEquals( $time, $task->snoozed_until );
}
/**
* Tests that a non snoozeable task cannot be snoozed.
*/
public function test_not_snoozeable() {
$task = new Task(
array(
'id' => 'wc-unit-test-snoozeable-task',
'is_snoozeable' => false,
)
);
$task->snooze();
$this->assertEquals( false, $task->is_snoozed() );
}
/**
* Tests that a task is no longer consider snoozed after the time has passed.
*/
public function test_snooze_time() {
$task = new Task(
array(
'id' => 'wc-unit-test-snoozeable-task',
'is_snoozeable' => true,
)
);
$time = time() * 1000 - 1;
$snoozed = get_option( Task::SNOOZED_OPTION, array() );
$snoozed['wc-unit-test-snoozeable-task'] = $time;
update_option( Task::SNOOZED_OPTION, $snoozed );
$this->assertEquals( false, $task->is_snoozed() );
}
/**
* Tests that a task's properties are returned as JSON.
*/
public function test_json() {
$task = new Task(
array(
'id' => 'wc-unit-test-task',
)
);
$json = $task->get_json();
$this->assertArrayHasKey( 'id', $json );
$this->assertArrayHasKey( 'title', $json );
$this->assertArrayHasKey( 'content', $json );
$this->assertArrayHasKey( 'actionLabel', $json );
$this->assertArrayHasKey( 'actionUrl', $json );
$this->assertArrayHasKey( 'isComplete', $json );
$this->assertArrayHasKey( 'isVisible', $json );
$this->assertArrayHasKey( 'time', $json );
$this->assertArrayHasKey( 'isDismissed', $json );
$this->assertArrayHasKey( 'isDismissable', $json );
$this->assertArrayHasKey( 'isSnoozed', $json );
$this->assertArrayHasKey( 'isSnoozeable', $json );
$this->assertArrayHasKey( 'snoozedUntil', $json );
}
}