full(); $rows = $kb->getRows(); self::assertCount(7, $rows, 'full keyboard has 7 rows when WEB_APP_URL is set'); // Flatten buttons. $buttons = []; foreach ($rows as $row) { foreach ($row->getButtons() as $button) { $buttons[] = $button; } } $types = array_map(fn($b) => $b->getType()->value, $buttons); self::assertContains('callback', $types); self::assertContains('link', $types); self::assertContains('message', $types); self::assertContains('clipboard', $types); self::assertContains('request_contact', $types); self::assertContains('request_geo_location', $types); self::assertContains('open_app', $types); } public function testFullKeyboardSkipsOpenAppWhenUrlMissing(): void { $kb = (new KeyboardFactory(null))->full(); $rows = $kb->getRows(); self::assertCount(6, $rows, 'OpenApp row is skipped without WEB_APP_URL'); $buttons = []; foreach ($rows as $row) { foreach ($row->getButtons() as $button) { $buttons[] = $button; } } $types = array_map(fn($b) => $b->getType()->value, $buttons); self::assertNotContains('open_app', $types); } public function testCallbackRowCoversAllIntents(): void { $kb = (new KeyboardFactory())->full(); $callbackRow = $kb->getRows()[0]; $buttons = $callbackRow->getButtons(); self::assertCount(3, $buttons); self::assertInstanceOf(CallbackButton::class, $buttons[0]); self::assertSame(Intent::Positive->value, $buttons[0]->intent->value); self::assertSame(Intent::Negative->value, $buttons[1]->intent->value); self::assertSame(Intent::Default->value, $buttons[2]->intent->value); } public function testJsonSerializationShape(): void { $kb = (new KeyboardFactory())->simpleCallback(); $rows = $kb->getRows(); self::assertCount(1, $rows); $buttons = $rows[0]->getButtons(); self::assertCount(2, $buttons); $first = $buttons[0]; self::assertInstanceOf(CallbackButton::class, $first); self::assertSame('vote:yes', $first->payload); $json = $kb->build()->jsonSerialize(); self::assertArrayHasKey('buttons', $json); self::assertCount(1, $json['buttons']); // Each row serializes as a list of {type, text, ...} objects. self::assertSame('vote:yes', $json['buttons'][0][0]['payload']); self::assertSame('vote:no', $json['buttons'][0][1]['payload']); } }