page = new BookingPage(); } /** * Renders the page as a logged-in, approved student — the path that * includes the template and its data attributes. * * @param array $atts */ private function renderForStudent(array $atts): string { Functions\when('is_user_logged_in')->justReturn(true); Functions\when('get_current_user_id')->justReturn(3); Functions\when('get_user_meta')->justReturn(''); Functions\when('current_user_can')->justReturn(true); Functions\when('wp_enqueue_style')->justReturn(null); Functions\when('wp_enqueue_script')->justReturn(null); Functions\when('wp_create_nonce')->justReturn('nonce123'); Functions\when('absint')->alias(static fn ($value) => abs((int) $value)); return $this->page->render($atts); } public function testLoggedOutVisitorIsLinkedToTheWordPressLoginByDefault(): void { Functions\when('is_user_logged_in')->justReturn(false); Functions\when('get_permalink')->justReturn('https://example.com/book/'); Functions\when('wp_login_url')->alias( static fn(string $redirect): string => 'https://example.com/wp-login.php?redirect_to=' . $redirect ); $html = $this->page->render([]); self::assertStringContainsString( 'href="https://example.com/wp-login.php?redirect_to=https://example.com/book/"', $html ); self::assertStringContainsString('log in to book a lesson', $html); } public function testLoggedOutVisitorIsLinkedToTheChosenLoginPage(): void { Functions\when('is_user_logged_in')->justReturn(false); Functions\when('get_permalink')->alias( static fn(int $id = 0): string|false => 5 === $id ? 'https://example.com/login/' : false ); Functions\expect('wp_login_url')->never(); $html = $this->page->render(['loginPageId' => 5]); self::assertStringContainsString('href="https://example.com/login/"', $html); } public function testShortcodeStyleAttributeSelectsTheLoginPageToo(): void { Functions\when('is_user_logged_in')->justReturn(false); Functions\when('get_permalink')->alias( static fn(int $id = 0): string|false => 5 === $id ? 'https://example.com/login/' : false ); $html = $this->page->render(['login_page_id' => '5']); self::assertStringContainsString('href="https://example.com/login/"', $html); } public function testDefaultEmbedShowsBothHalvesWithTheFilterAndNoPinnedType(): void { $html = $this->renderForStudent([]); self::assertStringContainsString('id="us-booking-app"', $html); self::assertStringContainsString('id="us-my-lessons"', $html); self::assertStringContainsString('id="us-slot-list"', $html); self::assertStringNotContainsString('data-lesson-type', $html); self::assertStringNotContainsString('data-type-filter', $html); } public function testBlockLessonTypeAttributePinsASingleType(): void { self::assertStringContainsString( 'data-lesson-type="12"', $this->renderForStudent(['lessonTypeId' => 12]) ); } public function testShortcodeLessonTypeAttributePinsASingleType(): void { self::assertStringContainsString( 'data-lesson-type="7"', $this->renderForStudent(['lesson_type' => '7']) ); } public function testGarbageLessonTypeAttributeIsIgnored(): void { self::assertStringNotContainsString( 'data-lesson-type', $this->renderForStudent(['lesson_type' => 'banana']) ); } public function testFilterCanBeTurnedOffByBlockAndShortcodeAlike(): void { self::assertStringContainsString( 'data-type-filter="0"', $this->renderForStudent(['showTypeFilter' => false]) ); // "no" is truthy to PHP, so the shortcode wording is matched explicitly. self::assertStringContainsString( 'data-type-filter="0"', $this->renderForStudent(['show_filter' => 'no']) ); self::assertStringNotContainsString( 'data-type-filter', $this->renderForStudent(['show_filter' => 'yes']) ); } public function testBookingOnlyEmbedLeavesOutTheUpcomingLessons(): void { $html = $this->renderForStudent(['displayMode' => 'booking']); self::assertStringContainsString('id="us-slot-list"', $html); self::assertStringNotContainsString('us-my-lessons', $html); } public function testUpcomingOnlyEmbedLeavesOutTheBookingCalendar(): void { $html = $this->renderForStudent(['show' => 'upcoming']); self::assertStringContainsString('id="us-my-lessons"', $html); self::assertStringNotContainsString('us-slot-list', $html); self::assertStringNotContainsString('us-booking-confirmation', $html); } public function testUnknownDisplayModeShowsTheWholePage(): void { $html = $this->renderForStudent(['displayMode' => 'sideways']); self::assertStringContainsString('id="us-my-lessons"', $html); self::assertStringContainsString('id="us-slot-list"', $html); } public function testLoginUrlFallsBackToWordPressLoginWhenThePageIsGone(): void { // The chosen page was deleted: get_permalink() returns false for it // and for the current (test) context alike. Functions\when('get_permalink')->justReturn(false); Functions\when('wp_login_url')->alias( static fn(string $redirect): string => '' === $redirect ? 'https://example.com/wp-login.php' : 'https://example.com/wp-login.php?redirect_to=' . $redirect ); self::assertSame('https://example.com/wp-login.php', $this->page->loginUrl(5)); } }