Skip to content
12 changes: 12 additions & 0 deletions config/pixelify.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,16 @@
|
*/
'debug' => env('FACEBOOK_PIXEL_DEBUG', false),

/*
|--------------------------------------------------------------------------
| Queued Listener Mode
|--------------------------------------------------------------------------
|
| If true, the SendPixelEvent listener will be queued, allowing you to handle pixel events asynchronously.
| Make sure to set up a queue worker to process the jobs. If false, the listener will execute synchronously,
| which may impact performance if there are many events.
|
*/
'queued_listener_mode' => env('FACEBOOK_QUEUED_LISTENER_MODE', false),
];
8 changes: 7 additions & 1 deletion src/Listeners/SendPixelEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@

namespace Revoltify\Pixelify\Listeners;

use Illuminate\Contracts\Queue\ShouldQueue;
use Revoltify\Pixelify\Events\PixelEventOccurred;
use Revoltify\Pixelify\Http\Client\FacebookClient;

final readonly class SendPixelEvent
final readonly class SendPixelEvent implements ShouldQueue
{
public function __construct(private FacebookClient $client) {}

public function handle(PixelEventOccurred $event): void
{
$this->client->sendEvent($event->eventData);
}

public function shouldQueue(PixelEventOccurred $event): bool
{
return config()->boolean('pixelify.queued_listener_mode', false);
}
}
129 changes: 68 additions & 61 deletions tests/Feature/PixelifyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,78 +2,85 @@

declare(strict_types=1);

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cookie;
use Illuminate\Support\Facades\Event;
use Revoltify\Pixelify\DTO\EventData;
use Revoltify\Pixelify\DTO\ProductData;
use Revoltify\Pixelify\DTO\UserData;
use Revoltify\Pixelify\Events\PixelEventOccurred;
use Revoltify\Pixelify\Facades\Pixelify;

beforeEach(function (): void {
Event::fake();
});

test('it can track page view event', function (): void {
$userData = new UserData(
firstName: 'John',
lastName: 'Doe',
email: 'test@example.com'
);

Pixelify::pageView($userData);

Event::assertDispatched(PixelEventOccurred::class, fn ($event): bool => $event->eventData->eventName === 'PageView');
});

test('it can track view content event', function (): void {
$productData = new ProductData(
productId: '123',
price: 99.99,
);

Pixelify::viewContent($productData);

Event::assertDispatched(PixelEventOccurred::class, fn ($event): bool => $event->eventData->eventName === 'ViewContent'
&& $event->eventData->productData->productId === '123');
use Revoltify\Pixelify\Http\Middleware\FacebookTrackingMiddleware;
use Revoltify\Pixelify\Listeners\SendPixelEvent;

dataset('listener modes', [
'queued mode' => [true],
'sync mode' => [false],
]);

dataset('tracking scenarios', [
'page view' => [
fn (): mixed => Pixelify::pageView(new UserData(firstName: 'John', lastName: 'Doe', email: 'test@example.com')),
fn (PixelEventOccurred $event): bool => $event->eventData->eventName === 'PageView',
],
'view content' => [
fn (): mixed => Pixelify::viewContent(new ProductData(productId: '123', price: 99.99)),
fn (PixelEventOccurred $event): bool => $event->eventData->eventName === 'ViewContent'
&& $event->eventData->productData->productId === '123',
],
'add to cart' => [
fn (): mixed => Pixelify::addToCart(new ProductData(productId: '123', price: 99.99, quantity: 2)),
fn (PixelEventOccurred $event): bool => $event->eventData->eventName === 'AddToCart'
&& $event->eventData->productData->quantity === 2,
],
'initiate checkout' => [
fn (): mixed => Pixelify::initiateCheckout(
new ProductData(productId: '123', price: 99.99),
new UserData(email: 'test@example.com')
),
fn (PixelEventOccurred $event): bool => $event->eventData->eventName === 'InitiateCheckout'
&& $event->eventData->userData->email === 'test@example.com',
],
'purchase' => [
fn (): mixed => Pixelify::purchase(new ProductData(productId: '123', price: 99.99, currency: 'USD')),
fn (PixelEventOccurred $event): bool => $event->eventData->eventName === 'Purchase'
&& $event->eventData->productData->currency === 'USD',
],
]);

describe('event tracking', function (): void {
beforeEach(function (): void {
Event::fake();
});

test('it dispatches the expected pixel event', function (bool $isQueued, Closure $trackEvent, Closure $assertEvent): void {
config()->set('pixelify.queued_listener_mode', $isQueued);

$trackEvent();

Event::assertDispatched(PixelEventOccurred::class, fn (PixelEventOccurred $event): bool => $assertEvent($event));
})->with('listener modes')->with('tracking scenarios');
});

test('it can track add to cart event', function (): void {
$productData = new ProductData(
productId: '123',
price: 99.99,
quantity: 2
);

Pixelify::addToCart($productData);

Event::assertDispatched(PixelEventOccurred::class, fn ($event): bool => $event->eventData->eventName === 'AddToCart'
&& $event->eventData->productData->quantity === 2);
});

test('it can track initiate checkout event', function (): void {
$productData = new ProductData(
productId: '123',
price: 99.99
);

$userData = new UserData(
email: 'test@example.com'
);
describe('listener queueing', function (): void {
test('it follows the configured queue mode', function (bool $isQueued): void {
config()->set('pixelify.queued_listener_mode', $isQueued);

Pixelify::initiateCheckout($productData, $userData);
$listener = app(SendPixelEvent::class);
$event = new PixelEventOccurred(new EventData(eventName: 'PageView', eventId: 'test-id'));

Event::assertDispatched(PixelEventOccurred::class, fn ($event): bool => $event->eventData->eventName === 'InitiateCheckout'
&& $event->eventData->userData->email === 'test@example.com');
expect($listener->shouldQueue($event))->toBe($isQueued);
})->with('listener modes');
});

test('it can track purchase event', function (): void {
$productData = new ProductData(
productId: '123',
price: 99.99,
currency: 'USD'
);
describe('middleware tracking', function (): void {
test('it queues tracking cookies', function (bool $isQueued): void {
config()->set('pixelify.queued_listener_mode', $isQueued);

Pixelify::purchase($productData);
$middleware = app(FacebookTrackingMiddleware::class);
$middleware->handle(Request::create('/?fbclid=test123'), fn ($request) => response('ok'));

Event::assertDispatched(PixelEventOccurred::class, fn ($event): bool => $event->eventData->eventName === 'Purchase'
&& $event->eventData->productData->currency === 'USD');
expect(Cookie::queued('_fbc'))->not->toBeNull();
expect(Cookie::queued('_fbp'))->not->toBeNull();
})->with('listener modes');
});
Loading