App-TimeTracker
view release on metacpan or search on metacpan
t/Command/core.t view on Meta::CPAN
use 5.010;
use strict;
use warnings;
use lib qw(t);
use Test::Most;
use Test::Trap;
use Test::File;
use testlib::FakeHomeDir;
use App::TimeTracker::Proto;
local $ENV{TZ} = 'UTC';
my $tmp = testlib::Fixtures::setup_tempdir;
my $home = $tmp->subdir('.TimeTracker');
$tmp->subdir('some_project')->mkpath;
$tmp->subdir('other_project')->mkpath;
$tmp->subdir('project_name_auto')->mkpath;
$tmp->subdir('project_name_custom')->mkpath;
my $p = App::TimeTracker::Proto->new( home => $home );
my $now = DateTime->now;
$now->set_time_zone('local');
my $basetf = $now->ymd('') . '-';
my $tracker_dir = $home->subdir( $now->year, sprintf( "%02d", $now->month ) );
{ # init
@ARGV = ('init');
my $class = $p->setup_class( {} );
file_exists_ok( $home->file('projects.json') );
file_exists_ok( $home->file('tracker.json') );
file_not_exists_ok( $tmp->file( 'some_project', '.tracker.json' ) );
file_not_exists_ok( $tmp->file( 'other_project', '.tracker.json' ) );
$p->_build_home($home);
my $t = $class->name->new( home => $home, config => {}, _current_project => 'some_project' );
trap { $t->cmd_init( $tmp->subdir('some_project') ) };
is( $trap->stdout, "Set up this directory for time-tracking via file .tracker.json\n",
'init: output' );
file_exists_ok( $home->file('projects.json') );
file_exists_ok( $home->file('tracker.json') );
file_exists_ok( $tmp->file( 'some_project', '.tracker.json' ) );
}
{ # init (setting project name)
file_not_exists_ok( $tmp->file( 'project_name_auto', '.tracker.json' ) );
file_not_exists_ok( $tmp->file( 'project_name_custom', '.tracker.json' ) );
@ARGV = ('init');
my $class = $p->setup_class( {} );
{
my $p = App::TimeTracker::Proto->new( home => $home );
# _current_project not set
my $t = $class->name->new( home => $home, config => {} );
trap { $t->cmd_init( $tmp->subdir('project_name_auto') ) };
file_exists_ok( $tmp->file( 'project_name_auto', '.tracker.json' ) );
my $config = $p->load_config( $tmp->subdir(qw(project_name_auto)) );
is $config->{project}, 'project_name_auto', 'automatic project name';
is $p->project, 'project_name_auto', 'project attribute set correctly';
}
{
my $p = App::TimeTracker::Proto->new( home => $home );
# _current_project is set
my $t = $class->name->new( home => $home, config => {}, _current_project => 'my-custom-project' );
trap { $t->cmd_init( $tmp->subdir('project_name_custom') ) };
file_exists_ok( $tmp->file( 'project_name_custom', '.tracker.json' ) );
my $config = $p->load_config( $tmp->subdir(qw(project_name_custom)) );
is $config->{project}, 'my-custom-project', 'custom project name';
is $p->project, 'my-custom-project', 'project attribute set from file';
}
}
{ # init (setting up project tree)
@ARGV = ('init');
my $class = $p->setup_class( {} );
my $project_dir = $tmp->subdir('project-top');
my $subproj0_dir = $project_dir->subdir('subproj0');
$subproj0_dir->mkpath;
my $subproj1_dir = $project_dir->subdir('subproj1');
$subproj1_dir->mkpath;
{
my $p = App::TimeTracker::Proto->new( home => $home );
# _current_project not set
{
my $t = $class->name->new( home => $home, config => {}, _current_project => 'top' );
trap { $t->cmd_init( $project_dir ) };
}
{
my $t = $class->name->new( home => $home, config => {}, _current_project => 'sub' );
trap { $t->cmd_init( $subproj0_dir ) };
}
{
my $t = $class->name->new( home => $home, config => {} );
trap { $t->cmd_init( $subproj1_dir ) };
# set config without "project" key so that last component of path
# must be used
$subproj1_dir->file('.tracker.json')->spew("{}");
}
my $tracker_files = superhashof({
'top' => "" . $project_dir->file('.tracker.json'),
'sub' => "" . $subproj0_dir->file('.tracker.json'),
'subproj1' => "" . $subproj1_dir->file('.tracker.json'),
});
my $projects_data;
$projects_data = $p->slurp_projects;
cmp_deeply $projects_data, $tracker_files, 'after init of all projects';
$p->load_config( $project_dir );
$projects_data = $p->slurp_projects;
cmp_deeply $projects_data, $tracker_files, 'after loading top project';
$p->load_config( $subproj0_dir );
$projects_data = $p->slurp_projects;
cmp_deeply $projects_data, $tracker_files, 'after loading sub project';
$p->load_config( $subproj1_dir );
$projects_data = $p->slurp_projects;
cmp_deeply $projects_data, $tracker_files, 'after loading subproj1 project';
}
}
my $c1 = $p->load_config( $tmp->subdir(qw(some_project)) );
{ # start
@ARGV = ('start');
my $class = $p->setup_class($c1);
file_not_exists_ok( $tracker_dir->file( $basetf . '140000_some_project.trc' ),
'tracker file does not exist yet' );
my $t = $class->name->new(
home => $home,
config => $c1,
_current_project => 'some_project',
at => '14:00'
);
trap { $t->cmd_start };
is( $trap->stdout, "Started working on some_project at 14:00:00\n", 'start: output' );
file_not_empty_ok( $tracker_dir->file( $basetf . '140000_some_project.trc' ),
'tracker file exists' );
}
{ # current
# # TODO: need to monkey-patch $class->now to return a mocked value
@ARGV = ('current');
my $class = $p->setup_class($c1);
my $t = $class->name->new( home => $home, config => $c1, _current_project => 'some_project' );
trap { $t->cmd_current };
like(
$trap->stdout,
qr/^Working \d{2}:\d{2}:\d{2} on some_project/,
'current project is some_project'
);
like( $trap->stdout, qr/Started at 14:00:00/, 'project start time is correct' );
}
{ # stop
@ARGV = ('stop');
my $class = $p->setup_class($c1);
my $t = $class->name->new(
home => $home,
config => $c1,
_current_project => 'some_project',
at => '14:15'
);
trap { $t->cmd_stop };
is( $trap->stdout, "Worked 00:15:00 on some_project\n", 'stop: output' );
my $task = App::TimeTracker::Data::Task->load(
$tracker_dir->file( $basetf . '140000_some_project.trc' )->stringify );
is( $task->seconds, 15 * 60, 'task seconds' );
is( $task->duration, '00:15:00', 'task duration' );
trap { $t->cmd_current };
like( $trap->stdout, qr/Worked 15 minutes from 14:00:00 till 14:15:00/, '' );
}
{ # append
@ARGV = ('append');
my $class = $p->setup_class($c1);
my $t = $class->name->new( home => $home, config => $c1, _current_project => 'some_project' );
trap { $t->cmd_append };
is( $trap->stdout, "Started working on some_project at 14:15:00\n", 'stop: output' );
my $trc = $tracker_dir->file( $basetf . '141500_some_project.trc' );
file_not_empty_ok( $trc, 'tracker file exists' );
my $task = App::TimeTracker::Data::Task->load( $trc->stringify );
is( $task->stop, undef, 'task stop not set' );
}
{ # init other project
file_not_exists_ok( $tmp->file( 'other_project', '.tracker.json' ) );
@ARGV = ('init');
my $class = $p->setup_class( {} );
my $t = $class->name->new( home => $home, config => {}, _current_project => 'other_project' );
trap { $t->cmd_init( $tmp->subdir('other_project') ) };
is( $trap->stdout, "Set up this directory for time-tracking via file .tracker.json\n",
'init: output' );
file_exists_ok( $tmp->file( 'other_project', '.tracker.json' ) );
}
my $c2 = $p->load_config( $tmp->subdir(qw(other_project)) );
{ # start other project
@ARGV = ('start');
my $class = $p->setup_class($c2);
my $trc = $tracker_dir->file( $basetf . '143000_other_project.trc' );
file_not_exists_ok( $trc, 'tracker file does not exist yet' );
my $t = $class->name->new(
home => $home,
config => $c2,
_current_project => 'other_project',
at => '14:30'
);
trap { $t->cmd_start };
is( $trap->stdout,
"Worked 00:15:00 on some_project\nStarted working on other_project at 14:30:00\n",
'start: output'
);
file_not_empty_ok( $trc, 'tracker file exists' );
my $task = App::TimeTracker::Data::Task->load(
$tracker_dir->file( $basetf . '141500_some_project.trc' )->stringify );
is( $task->seconds, 15 * 60, 'prev task seconds' );
is( $task->duration, '00:15:00', 'prev task duration' );
}
{ # stop it
@ARGV = ('stop');
my $class = $p->setup_class($c1);
my $t = $class->name->new(
home => $home,
config => $c2,
_current_project => 'other_project',
at => '14:45'
);
trap { $t->cmd_stop };
like( $trap->stdout, qr/00:15:00.*other_project/, 'stop: output' );
}
{ # version
@ARGV = ('version');
my $version = App::TimeTracker->VERSION;
my $class = $p->setup_class($c1);
my $t = $class->name->new( home => $home, config => $c2 );
trap { $t->cmd_version };
like( $trap->stdout, qr/$version/, 'version: output' );
}
{ # commands
@ARGV = ('commands');
my $class = $p->setup_class( {} );
my $t = $class->name->new( home => $home, config => {} );
trap { $t->cmd_commands };
my @core_commands = qw<
append
commands
continue
current
init
list
plugins
recalc_trackfile
report
show_config
start
stop
version
worked>;
my $output = $trap->stdout;
my @lines = split /\n/, $output;
@lines = grep !/Available commands/, @lines;
s/^\s+//g foreach @lines; # remove leading whitespace from strings
is_deeply( \@lines, \@core_commands, 'default list of core commands is sorted' );
}
{ # report global
@ARGV = ('report');
my $class = $p->setup_class( $c1, 'report' );
( run in 1.020 second using v1.01-cache-2.11-cpan-39bf76dae61 )