diff --git a/DEVELOPPING.md b/DEVELOPPING.md index 6406815..08edd2a 100644 --- a/DEVELOPPING.md +++ b/DEVELOPPING.md @@ -120,4 +120,4 @@ Maintaining code quality by adding the custom post-commit hook to yours. ```bash cat ./scripts/hooks/post-commit >> ./.git/hooks/post-commit -``` \ No newline at end of file +``` diff --git a/commerce_google_tag_manager.info.yml b/commerce_google_tag_manager.info.yml index 5f62e79..ce11218 100644 --- a/commerce_google_tag_manager.info.yml +++ b/commerce_google_tag_manager.info.yml @@ -6,4 +6,4 @@ type: module dependencies: - drupal:google_tag - drupal:commerce -- drupal:commerce_checkout \ No newline at end of file +- drupal:commerce_checkout diff --git a/tests/src/Kernel/AlterProductEventTest.php b/tests/src/Kernel/AlterProductEventTest.php new file mode 100644 index 0000000..33c3257 --- /dev/null +++ b/tests/src/Kernel/AlterProductEventTest.php @@ -0,0 +1,70 @@ +variation = ProductVariation::create([ + 'type' => 'default', + 'sku' => $this->randomString(10), + 'status' => TRUE, + ]); + $this->variation->save(); + + $this->product = new Product(); + $this->product + ->setName($this->randomString(16)) + ->setId(1) + ->setVariant($this->variation->getTitle()) + ->setPrice('11.99'); + } + + /** + * @covers ::getProduct + */ + public function testGetProduct() { + $event = new AlterProductEvent($this->product, $this->variation); + $this->assertInstanceOf(Product::class, $event->getProduct()); + } + + /** + * @covers ::getProductVariation + */ + public function testGetProductVariation() { + $event = new AlterProductEvent($this->product, $this->variation); + $this->assertInstanceOf(ProductVariationInterface::class, $event->getProductVariation()); + } + +} diff --git a/tests/src/Kernel/CommerceKernelTestBase.php b/tests/src/Kernel/CommerceKernelTestBase.php new file mode 100644 index 0000000..4c502ce --- /dev/null +++ b/tests/src/Kernel/CommerceKernelTestBase.php @@ -0,0 +1,32 @@ +installEntitySchema('commerce_product_variation'); + $this->installEntitySchema('commerce_product'); + $this->installConfig(['commerce_product']); + } + +} diff --git a/tests/src/Kernel/EventStorageServiceTest.php b/tests/src/Kernel/EventStorageServiceTest.php new file mode 100644 index 0000000..26769d5 --- /dev/null +++ b/tests/src/Kernel/EventStorageServiceTest.php @@ -0,0 +1,182 @@ +installSchema('system', ['key_value_expire']); + + $this->tempStore = $this->container->get('tempstore.private')->get('commerce_google_tag_manager'); + $this->eventStorage = $this->container->get('commerce_google_tag_manager.event_storage'); + + $this->detailEvent = [ + 'event' => EventTrackerService::EVENT_PRODUCT_DETAIL_VIEWS, + 'ecommerce' => [ + 'detail' => [ + 'actionField' => [ + 'list' => '', + ], + 'products' => [ + 0 => [ + 'name' => 'Lorem Ipsum', + 'id' => '1', + 'price' => '11.99', + 'variant' => 'Lorem Ipsum', + ], + ], + ], + ], + ]; + + $this->checkoutEvent = [ + 'event' => EventTrackerService::EVENT_CHECKOUT, + 'ecommerce' => [ + 'checkout' => [ + 'actionField' => [ + 'step' => 1, + ], + 'products' => [ + 0 => [ + 'name' => 'Lorem Ipsum', + 'id' => '1', + 'price' => '11.99', + 'variant' => 'Lorem Ipsum', + 'quantity' => 1, + ], + ], + ], + ], + ]; + } + + /** + * @covers ::getEvents + */ + public function testGetEvents() { + $this->tempStore->set('events', [ + '0e05cdf318b5832a7caf62ad11d386f4' => $this->detailEvent, + ]); + $result = $this->eventStorage->getEvents(); + $this->assertSame([0 => $this->detailEvent], $result); + } + + /** + * @covers ::getEvents + */ + public function testGetEventsEmpty() { + $this->tempStore->set('events', NULL); + $result = $this->eventStorage->getEvents(); + $this->assertInternalType('array', $result); + $this->assertEmpty($result); + } + + /** + * @covers ::flush + */ + public function testFlush() { + $this->tempStore->set('events', [ + '0e05cdf318b5832a7caf62ad11d386f4' => $this->detailEvent, + ]); + $this->eventStorage->flush(); + $result = $this->tempStore->get('events'); + $this->assertNull($result); + } + + /** + * @covers ::addEvent + * + * Asserts usage of Private Temp Store for events queue. + */ + public function testAddEvent() { + $this->eventStorage->addEvent($this->detailEvent); + + $result = $this->tempStore->get('events'); + $this->assertInternalType('array', $result); + $this->assertSame([ + '0e05cdf318b5832a7caf62ad11d386f4' => $this->detailEvent, + ], $result); + } + + /** + * @covers ::addEvent + * + * Asserts the events queue follows the FIFO (First in First out) pattern. + */ + public function testAddEventFifoQueue() { + $this->eventStorage->addEvent($this->detailEvent); + $this->eventStorage->addEvent($this->checkoutEvent); + $events = $this->tempStore->get('events'); + + $this->assertSame([ + '0e05cdf318b5832a7caf62ad11d386f4' => $this->detailEvent, + '5d92a6ab1f5bd49c7ac5a065302dcb16' => $this->checkoutEvent, + ], $events); + } + + /** + * @covers ::addEvent + * + * Asserts strictly same event aren't added twice in the events queue. + */ + public function testAddEventSameSkipped() { + $this->eventStorage->addEvent($this->detailEvent); + $this->eventStorage->addEvent($this->detailEvent); + $events = $this->tempStore->get('events'); + $this->assertSame([ + '0e05cdf318b5832a7caf62ad11d386f4' => $this->detailEvent, + ], $events); + } + +} diff --git a/tests/src/Unit/AlterEventDataEventTest.php b/tests/src/Unit/AlterEventDataEventTest.php new file mode 100644 index 0000000..71accdd --- /dev/null +++ b/tests/src/Unit/AlterEventDataEventTest.php @@ -0,0 +1,184 @@ +alterEventData = new AlterEventDataEvent([]); + } + + /** + * @covers ::setEventData + * @covers ::getEventData + * + * @dataProvider eventData + */ + public function testSetEventDetailViews($event_data) { + $this->alterEventData->setEventData($event_data); + $this->assertSame($event_data, $this->alterEventData->getEventData()); + } + + /** + * List of supported event data. + * + * @return array + * Examples of event data structure by event types. + */ + public function eventData() { + return [ + 'Product detail views' => [ + [ + 'event' => EventTrackerService::EVENT_PRODUCT_DETAIL_VIEWS, + 'ecommerce' => [ + 'detail' => [ + 'actionField' => [ + 'list' => '', + ], + 'products' => [ + 0 => [ + 'name' => 'Lorem Ipsum', + 'id' => '1', + 'price' => '11.99', + 'variant' => 'Lorem Ipsum', + ], + ], + ], + ], + ], + ], + + // @todo add product impression example. + 'Product impressions' => [ + [ + 'event' => EventTrackerService::EVENT_PRODUCT_IMPRESSIONS, + 'ecommerce' => [], + ], + ], + + // @todo add product click example. + 'Product click' => [ + [ + 'event' => EventTrackerService::EVENT_PRODUCT_CLICK, + 'ecommerce' => [], + ], + ], + + 'Add to cart' => [ + [ + 'event' => EventTrackerService::EVENT_ADD_CART, + 'ecommerce' => [ + 'currencyCode' => 'CHF', + 'add' => [ + 'products' => [ + 0 => [ + 'name' => 'Lorem Ipsum', + 'id' => '1', + 'price' => '11.99', + 'variant' => 'Lorem Ipsum', + 'quantity' => 1, + ], + ], + ], + ], + ], + ], + + 'Remove to cart' => [ + [ + 'event' => EventTrackerService::EVENT_REMOVE_CART, + 'ecommerce' => [ + 'remove' => [ + 'products' => [ + 0 => [ + 'name' => 'Lorem Ipsum', + 'id' => '1', + 'price' => '11.99', + 'variant' => 'Lorem Ipsum', + 'quantity' => 1, + ], + ], + ], + ], + ], + ], + + 'Checkout' => [ + [ + 'event' => EventTrackerService::EVENT_CHECKOUT, + 'ecommerce' => [ + 'checkout' => [ + 'actionField' => [ + 'step' => 1, + ], + 'products' => [ + 0 => [ + 'name' => 'Lorem Ipsum', + 'id' => '1', + 'price' => '11.99', + 'variant' => 'Lorem Ipsum', + 'quantity' => 1, + ], + ], + ], + ], + ], + ], + + // @todo add checkout option example. + 'Checkout option' => [ + [ + 'event' => EventTrackerService::EVENT_CHECKOUT_OPTION, + 'ecommerce' => [], + ], + ], + + 'Purchase' => [ + [ + 'event' => EventTrackerService::EVENT_PURCHASE, + 'ecommerce' => [ + 'purchase' => [ + 'actionField' => [ + 'id' => '1', + 'affiliation' => 'Commerce Website', + 'revenue' => '11.99', + ], + 'products' => [ + 0 => [ + 'name' => 'Lorem Ipsum', + 'id' => '1', + 'price' => '11.99', + 'variant' => 'Lorem Ipsum', + 'quantity' => 1, + ], + ], + ], + ], + ], + ], + ]; + } + +} diff --git a/tests/src/Unit/EnhancedEcommerceEventsTest.php b/tests/src/Unit/EnhancedEcommerceEventsTest.php new file mode 100644 index 0000000..02b686d --- /dev/null +++ b/tests/src/Unit/EnhancedEcommerceEventsTest.php @@ -0,0 +1,49 @@ +assertEquals($expected, $event_name); + } + + /** + * List of supported event with expected names. + * + * @return array + * The list of CONST names & string expected value. + */ + public function eventNames() { + return [ + [ + EnhancedEcommerceEvents::ALTER_PRODUCT, + 'commerce_google_tag_manager.alter_product', + ], + [ + EnhancedEcommerceEvents::ALTER_EVENT_DATA, + 'commerce_google_tag_manager.alter_event_data', + ], + [ + EnhancedEcommerceEvents::TRACK_CHECKOUT_STEP, + 'commerce_google_tag_manager.track_checkout_step', + ], + ]; + } + +} diff --git a/tests/src/Unit/EventStorageServiceTest.php b/tests/src/Unit/EventStorageServiceTest.php new file mode 100644 index 0000000..6eb2a03 --- /dev/null +++ b/tests/src/Unit/EventStorageServiceTest.php @@ -0,0 +1,163 @@ +tempStore = $this->getMockBuilder(PrivateTempStoreFactory::class) + ->disableOriginalConstructor()->getMock(); + $this->eventDispatcher = $this->getMockBuilder(EventDispatcherInterface::class) + ->disableOriginalConstructor()->getMock(); + + $this->eventStorage = new EventStorageService($this->tempStore, $this->eventDispatcher); + + $this->event = [ + 'event' => EventTrackerService::EVENT_PRODUCT_DETAIL_VIEWS, + 'ecommerce' => [ + 'detail' => [ + 'actionField' => [ + 'list' => '', + ], + 'products' => [ + 0 => [ + 'name' => 'Lorem Ipsum', + 'id' => '1', + 'price' => '11.99', + 'variant' => 'Lorem Ipsum', + ], + ], + ], + ], + ]; + $this->alterEventDataEvent = new AlterEventDataEvent($this->event); + } + + /** + * @covers ::hash + */ + public function testHash() { + $result = $this->invokeMethod($this->eventStorage, 'hash', [$this->event]); + $this->assertEquals('0e05cdf318b5832a7caf62ad11d386f4', $result); + } + + /** + * When addEvent called on a new event, we should fire an Event Alter. + * + * This should occure only when the added event has not already been added. + * This will then dispatch EnhancedEcommerceEvents::ALTER_EVENT_DATA. + * + * @covers ::addEvent + */ + public function testAddEventDispatchAlter() { + // Mock stored events, make sure the event has never been added. + $stored_events = $this->getMockBuilder(PrivateTempStore::class) + ->disableOriginalConstructor()->getMock(); + $stored_events->expects($this->once()) + ->method('get') + ->with('events') + ->willReturn([]); + + // Re-Mock the Private Temp Store & The Symfony Event Dispatcher. + $this->tempStore->expects($this->once()) + ->method('get') + ->with('commerce_google_tag_manager') + ->willReturn($stored_events); + $this->eventDispatcher->expects($this->once()) + ->method('dispatch') + ->with(EnhancedEcommerceEvents::ALTER_EVENT_DATA, $this->alterEventDataEvent); + + $this->eventStorage = new EventStorageService($this->tempStore, $this->eventDispatcher); + + $this->eventStorage->addEvent($this->event); + } + + /** + * When addEvent called on a un-new event, we should not fire an Event Alter. + * + * This should occure only when the added event has already been added. + * This will then not dispatch EnhancedEcommerceEvents::ALTER_EVENT_DATA. + * + * @covers ::addEvent + */ + public function testAddEventShouldDispatchAlterOnlyOnce() { + // Mock stored events, make sure the event has already been added. + $stored_events = $this->getMockBuilder(PrivateTempStore::class) + ->disableOriginalConstructor()->getMock(); + $stored_events->expects($this->once()) + ->method('get') + ->with('events') + ->willReturn(['0e05cdf318b5832a7caf62ad11d386f4' => $this->event]); + + // Re-Mock the Private Temp Store & The Symfony Event Dispatcher. + $this->tempStore->expects($this->once()) + ->method('get') + ->with('commerce_google_tag_manager') + ->willReturn($stored_events); + $this->eventDispatcher->expects($this->never()) + ->method('dispatch'); + + $this->eventStorage = new EventStorageService($this->tempStore, $this->eventDispatcher); + + $this->eventStorage->addEvent($this->event); + } + +}