Skip to content
Open
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
18 changes: 15 additions & 3 deletions Clockwork/Clockwork.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,12 @@ public function resolveRequest()
// Resolve the current request as a "command" type request with command-specific data
public function resolveAsCommand($name, $exitCode = null, $arguments = [], $options = [], $argumentsDefaults = [], $optionsDefaults = [], $output = null)
{
$this->resolveRequest();
return $this->resolveRequest()->asCommand($name, $exitCode, $arguments, $options, $argumentsDefaults, $optionsDefaults, $output);
}

// Set the current request as a "command" type request with command-specific data
public function asCommand($name, $exitCode = null, $arguments = [], $options = [], $argumentsDefaults = [], $optionsDefaults = [], $output = null)
{
$this->request->type = RequestType::COMMAND;
$this->request->commandName = $name;
$this->request->commandArguments = $arguments;
Expand All @@ -81,8 +85,12 @@ public function resolveAsCommand($name, $exitCode = null, $arguments = [], $opti
// Resolve the current request as a "queue-job" type request with queue-job-specific data
public function resolveAsQueueJob($name, $description = null, $status = 'processed', $payload = [], $queue = null, $connection = null, $options = [])
{
$this->resolveRequest();
return $this->resolveRequest()->asQueueJob($name, $description, $status, $payload, $queue, $connection, $options);
}

// Set the current request as a "queue-job" type request with queue-job-specific data
public function asQueueJob($name, $description = null, $status = 'processed', $payload = [], $queue = null, $connection = null, $options = [])
{
$this->request->type = RequestType::QUEUE_JOB;
$this->request->jobName = $name;
$this->request->jobDescription = $description;
Expand All @@ -99,8 +107,12 @@ public function resolveAsQueueJob($name, $description = null, $status = 'process
// message in case of failure and array of ran asserts
public function resolveAsTest($name, $status = 'passed', $statusMessage = null, $asserts = [])
{
$this->resolveRequest();
return $this->resolveRequest()->asTest($name, $status, $statusMessage, $asserts);
}

// Set the current request as a "test" type request with test-specific data
public function asTest($name, $status = 'passed', $statusMessage = null, $asserts = [])
{
$this->request->type = RequestType::TEST;
$this->request->testName = $name;
$this->request->testStatus = $status;
Expand Down
114 changes: 85 additions & 29 deletions Clockwork/Support/Laravel/Tests/ClockworkExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,35 @@
class ClockworkExtension implements Runner\Extension\Extension
{
public static $asserts = [];
public static $tests = [];

public function bootstrap(
TextUI\Configuration\Configuration $configuration,
Runner\Extension\Facade $facade,
Runner\Extension\ParameterCollection $parameters
): void {
$subscribers = array_filter([

new class implements Event\Test\PreparedSubscriber {
public function notify($event): void { ClockworkExtension::$asserts = []; }
public function notify($event): void { ClockworkExtension::$asserts = []; ClockworkExtension::prepareTest($event->test()->id()); }
},
new class implements Event\Test\ErroredSubscriber {
public function notify($event): void { ClockworkExtension::recordTest('error', $event->throwable()->message()); }
public function notify($event): void { ClockworkExtension::finishTest($event->test()->id(), 'error', $event->throwable()->message()); }
},
new class implements Event\Test\FailedSubscriber {
public function notify($event): void { ClockworkExtension::recordTest('failed', $event->throwable()->message()); }
public function notify($event): void { ClockworkExtension::finishTest($event->test()->id(), 'failed', $event->throwable()->message()); }
},
new class implements Event\Test\MarkedIncompleteSubscriber {
public function notify($event): void { ClockworkExtension::recordTest('incomplete', $event->throwable()->message()); }
public function notify($event): void { ClockworkExtension::finishTest($event->test()->id(), 'incomplete', $event->throwable()->message()); }
},
new class implements Event\Test\PassedSubscriber {
public function notify($event): void { ClockworkExtension::recordTest('passed'); }
public function notify($event): void { ClockworkExtension::finishTest($event->test()->id(), 'passed'); }
},
new class implements Event\Test\SkippedSubscriber {
public function notify($event): void { ClockworkExtension::recordTest('skipped', $event->message()); }
public function notify($event): void { ClockworkExtension::finishTest($event->test()->id(), 'skipped', $event->message()); }
},
new class implements Event\Test\FinishedSubscriber {
public function notify($event): void { ClockworkExtension::storeTest($event->test()->id()); }
},
interface_exists(Event\Test\AssertionSucceededSubscriber::class) ? new class implements Event\Test\AssertionSucceededSubscriber {
public function notify($event): void { ClockworkExtension::recordAssertion(true); }
Expand All @@ -44,8 +49,10 @@ public function notify($event): void { ClockworkExtension::recordAssertion(false
$facade->registerSubscribers(...$subscribers);
}

public static function recordTest($status, $message = null)
public static function prepareTest($id)
{
if (static::isPrepared($id)) return;

$testCase = static::resolveTestCase();

if (! $testCase) return;
Expand All @@ -54,19 +61,74 @@ public static function recordTest($status, $message = null)

if (! $app) return;

if (! $app->make('clockwork.support')->isCollectingTests()) return;
if ($app->make('clockwork.support')->isTestFiltered($testCase->toString())) return;
$support = $app->make('clockwork.support');

if (! $support->isCollectingTests()) return;
if ($support->isTestFiltered($testCase->toString())) return;

static::$tests[$id] = [
'clockwork' => $app->make('clockwork'),
'name' => str_replace('__pest_evaluable_', '', $testCase->toString()),
'status' => 'passed',
'message' => null
];

static::beforeApplicationDestroyed($testCase, function () use ($id) {
static::resolveTest($id);
});
}

public static function finishTest($id, $status, $message = null)
{
static::prepareTest($id);

if (! static::isPrepared($id)) return;

static::$tests[$id]['status'] = $status;
static::$tests[$id]['message'] = $message;
}

public static function resolveTest($id)
{
if (! static::isPrepared($id) || static::isResolved($id)) return;

static::$tests[$id]['clockwork']->resolveRequest();
static::$tests[$id]['resolved'] = true;
}

$app->make('clockwork')
->resolveAsTest(
str_replace('__pest_evaluable_', '', $testCase->toString()),
$status,
$message,
static::$asserts
)
public static function storeTest($id)
{
if (! static::isPrepared($id)) return;

static::resolveTest($id);

$test = static::$tests[$id];

unset(static::$tests[$id]);

$test['clockwork']
->asTest($test['name'], $test['status'], $test['message'], static::$asserts)
->storeRequest();
}


protected static function isPrepared($id)
{
return isset(static::$tests[$id]);
}

protected static function isResolved($id)
{
return isset(static::$tests[$id]['resolved']);
}

protected static function beforeApplicationDestroyed($testCase, $callback)
{
// Call the protected beforeApplicationDestroyed method by binding a closure to the object
(function ($callback) {
$this->beforeApplicationDestroyed($callback);
})->call($testCase, $callback);
}

public static function recordAssertion($passed = true)
{
$trace = StackTrace::get([ 'arguments' => true, 'limit' => 10 ]);
Expand All @@ -93,17 +155,11 @@ protected static function resolveTestCase()

protected static function resolveApp($testCase)
{
$reflectionClass = new \ReflectionClass($testCase);

if ($reflectionClass->hasProperty('app')) {
$reflectionProperty = $reflectionClass->getProperty('app');
$reflectionProperty->setAccessible(true);

if ($reflectionProperty->getValue($testCase)) {
return $reflectionProperty->getValue($testCase);
}
} elseif (method_exists($testCase, 'createApplication')) {
return $testCase->createApplication();
}
if (! property_exists($testCase, 'app')) return;

// Retrieve the protected app property by binding a closure to the object
return (function () {
return $this->app;
})->call($testCase);
}
}