From 6e3affb1cbeaef70e6f55ccea5ca2f1fe6cb2bb4 Mon Sep 17 00:00:00 2001 From: James Griffin Date: Wed, 29 Jul 2026 22:31:28 -0300 Subject: [PATCH] Add an account block showing who is signed in MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [us_account], or the Account block: the signed-in visitor's name, their email, a Sign out link, and — only when the account books for someone besides itself — the students it books for. A parent's first question on seeing "signed in as Grace" is whether this is the account their children's lessons are on. Two decisions worth naming. Signed out with no login page chosen, the block renders nothing. Its whole subject is the person signed in, which a stranger is not, and a bare "you are not signed in" in a site header is noise with no way to act on it. With a login page chosen it offers a Sign in link instead. The editor preview is populated regardless, so the block is never an invisible box to the person placing it. Signing out returns to the chosen login page, or to the current page when there is none. A block meant for a header should not also navigate someone somewhere when they use it; the login page wins when configured, because the page they were on may well be members-only. The name comes from UserName::format(), so the block never exposes a username the way display_name can. Also brings docs/features/editor-blocks.md back in step: it still described "four shortcodes" and had never listed the family block. Closes #142 Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 3 + assets/css/frontend.css | 24 ++++ assets/js/blocks.js | 22 ++++ docs/features/editor-blocks.md | 34 +++++- src/Auth/AccountPage.php | 91 +++++++++++++++ src/BlockPreview.php | 26 +++++ src/BlockRegistrar.php | 20 ++++ src/Plugin.php | 6 +- src/ShortcodeRegistrar.php | 3 + templates/frontend/account-page.php | 38 +++++++ tests/Unit/Auth/AccountPageTest.php | 158 ++++++++++++++++++++++++++ tests/Unit/BlockRegistrarTest.php | 6 + tests/Unit/ShortcodeRegistrarTest.php | 6 +- 13 files changed, 430 insertions(+), 7 deletions(-) create mode 100644 src/Auth/AccountPage.php create mode 100644 templates/frontend/account-page.php create mode 100644 tests/Unit/Auth/AccountPageTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 105a748..12f66e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ each change under the current top section as you work. ## [1.3.1] +### Added +- An **Account** block (`[us_account]`) showing who is signed in — their name, their email, the students they book for if that is anyone besides themselves — and a **Sign out** link. Signing out returns to the login page chosen in the block, or to the page the visitor was already on when none is set, so putting it in a site header does not also move people somewhere. To a signed-out visitor it shows a **Sign in** link when a login page is chosen, and nothing at all when one is not: a panel about who is signed in has nothing to tell a stranger, and a notice they cannot act on is just clutter in a header. + ### Security - Signup now checks the password properly. The form scores it as you type with the same zxcvbn meter wp-admin uses and will not submit a weak one, and the server refuses — regardless of what the browser allowed — anything shorter than 8 characters, one of the well-known leaked passwords, one built from barely any distinct characters, or one containing your own name or email address. Composition rules ("must contain a symbol") are deliberately not imposed: they mostly produce predictable substitutions. Email addresses are validated on the server on every signup path, with a clear message when one is already registered. diff --git a/assets/css/frontend.css b/assets/css/frontend.css index 55f7e5a..6a3c8c5 100644 --- a/assets/css/frontend.css +++ b/assets/css/frontend.css @@ -549,6 +549,30 @@ color: #1a7d2e; } +/* + * The account panel: who is signed in, and the way out. Sized to sit in a + * header or sidebar, so the rules stay minimal and inherit the theme's type — + * a block that lands in a site header should look like it belongs there. + */ +.us-account p { + margin: 0 0 4px; +} + +.us-account-name { + font-weight: 600; +} + +.us-account-email, +.us-account-students { + display: block; + font-size: 0.9em; + opacity: 0.75; +} + +.us-account-actions { + margin-top: 8px; +} + /* Shown only in block-editor previews (see BlockPreview). */ .us-editor-note { font-size: 0.85em; diff --git a/assets/js/blocks.js b/assets/js/blocks.js index d643f28..dcaf81b 100644 --- a/assets/js/blocks.js +++ b/assets/js/blocks.js @@ -307,6 +307,28 @@ }) ), }, + { + name: 'us-scheduler/account', + title: __('Account', 'unsupervised-schedular'), + description: __('Shows who is signed in, who they book for, and a sign out link. Renders nothing for signed-out visitors unless a login page is chosen.', 'unsupervised-schedular'), + icon: 'admin-users', + keywords: ['account', 'sign out', 'log out', 'signed in', 'profile'], + shortcode: 'us_account', + attributes: { + loginPageId: { type: 'number', default: 0 }, + }, + inspector: (attributes, setAttributes) => el( + PanelBody, + { title: __('Signing in and out', 'unsupervised-schedular') }, + el(PageSelect, { + label: __('Login page', 'unsupervised-schedular'), + help: __('Where signing out returns to, and where signed-out visitors are offered a link to sign in. Without one, signing out returns to the current page and signed-out visitors see nothing.', 'unsupervised-schedular'), + defaultLabel: __('Stay on the current page', 'unsupervised-schedular'), + value: attributes.loginPageId, + onChange: (loginPageId) => setAttributes({ loginPageId }), + }) + ), + }, ]; blocks.forEach((def) => { diff --git a/docs/features/editor-blocks.md b/docs/features/editor-blocks.md index 3383256..c30a16d 100644 --- a/docs/features/editor-blocks.md +++ b/docs/features/editor-blocks.md @@ -1,8 +1,8 @@ # Editor Blocks -Gutenberg dynamic-block wrappers for the plugin's four front-end shortcodes, -so the pages can be previewed and styled inside the block editor instead of -appearing as grey shortcode text. +Gutenberg dynamic-block wrappers for the plugin's front-end shortcodes, so the +pages can be previewed and styled inside the block editor instead of appearing +as grey shortcode text. ## Blocks @@ -12,6 +12,8 @@ appearing as grey shortcode text. | `us-scheduler/student-login` | `[us_student_login]` | `Auth\LoginPage::render()` | | `us-scheduler/student-register` | `[us_student_register]` | `Auth\RegistrationPage::render()` | | `us-scheduler/group-classes` | `[us_group_classes]` | `GroupClass\GroupClassPage::render()` | +| `us-scheduler/family` | `[us_family]` | `Guardian\FamilyPage::render()` | +| `us-scheduler/account` | `[us_account]` | `Auth\AccountPage::render()` | The shortcodes remain registered for back-compat; blocks and shortcodes share the same page objects (constructed once in `Plugin::boot()`), so front-end @@ -21,7 +23,7 @@ transform. ## Block options -Four blocks have sidebar (inspector) options: +Most blocks have sidebar (inspector) options: | Block | Attribute | Default | Effect | |---|---|---|---| @@ -34,6 +36,8 @@ Four blocks have sidebar (inspector) options: | `us-scheduler/student-login` | `autoRedirect` (boolean) | `false` | Send logged-in visitors straight to the booking page instead of showing the link. Does nothing until a booking page is chosen. | | `us-scheduler/student-register` | `loginPageId` (number) | `0` | Page students continue to once registration finishes — the "Sign in to your account" link after they confirm their email, and the "Continue to your account" link an invited student gets on the spot. `0` = the WordPress login screen for the confirmation link, and no link at all for the (already signed-in) invited student. Shortcode equivalent: `[us_student_register login_page_id="…"]`. | | `us-scheduler/student-register` | `autoRedirect` (boolean) | `false` | Send students straight to that page instead of showing the link. Does nothing until a page is chosen — there is no login-screen fallback here. | +| `us-scheduler/family` | `loginPageId` (number) | `0` | Where visitors who are not signed in are sent to log in. Shortcode equivalent: `[us_family login_page_id="…"]`. | +| `us-scheduler/account` | `loginPageId` (number) | `0` | Where signing out returns to, and where a signed-out visitor is offered a **Sign in** link. `0` = signing out returns to the current page, and a signed-out visitor sees **nothing at all** — see below. Shortcode equivalent: `[us_account login_page_id="…"]`. | | `us-scheduler/group-classes` | `offeringId` (number) | `0` | Restrict the page to a single group class, for embedding on a page dedicated to that class. The class description is then omitted — only the schedule, instructor, price and enrolment controls are shown, so the surrounding page's own copy is not repeated. `0` = browse all classes, descriptions included. Shortcode equivalent: `[us_group_classes offering="…"]`. | The page selects list all published pages; if a chosen page is later deleted, @@ -105,6 +109,10 @@ placeholder content: - **Login** — the real `templates/frontend/login-page.php` template (it has no request-state dependencies). - **Registration** — a disabled sample of the `.us-register-form` fields. +- **Account** — a populated sample panel. Deliberately populated whatever the + editor user's own state: on the published page a signed-out visitor may see + nothing at all, and an empty box tells the person placing the block nothing + about where it will sit. Each preview starts with a `.us-editor-note` paragraph explaining what the published page shows instead. The note class only appears in editor previews. @@ -118,5 +126,23 @@ published page shows instead. The note class only appears in editor previews. and fallbacks. - `tests/Unit/Auth/LoginPageTest.php` — logged-in booking-link targets and fallbacks. +- `tests/Unit/Auth/AccountPageTest.php` — what each visitor sees, the + sign-out redirect target, and the signed-out empty render. - `tests/Unit/BlockPreviewTest.php` — preview markup mirrors the live CSS classes/ids and includes the editor note. + +## The account block's signed-out behaviour + +`us-scheduler/account` is the one block that can render **nothing**. It is meant +for a header, sidebar or account page, and its whole subject is the person +signed in — which a stranger is not. A bare "you are not signed in" in a site +header is noise that cannot be acted on, so: + +- **No login page chosen** → empty string for signed-out visitors. +- **Login page chosen** → a single **Sign in** link. + +Signed in, it shows the display name (`Auth\UserName::format()`, so a username +is never exposed), the account email, a **Sign out** link, and — only on an +account that books for someone other than itself — the students it books for. +Signing out returns to the chosen login page, or to the current page when there +is none, so a header sign-out does not also navigate the visitor somewhere. diff --git a/src/Auth/AccountPage.php b/src/Auth/AccountPage.php new file mode 100644 index 0000000..1ace546 --- /dev/null +++ b/src/Auth/AccountPage.php @@ -0,0 +1,91 @@ + $atts Block attributes (`loginPageId`) or + * shortcode attributes (`login_page_id`). + */ + public function render( array $atts ): string { + $loginPageId = Val::int( $atts['loginPageId'] ?? $atts['login_page_id'] ?? 0 ); + $loginUrl = $this->pageUrl( $loginPageId ); + + wp_enqueue_style( 'us-scheduler' ); + + if ( ! is_user_logged_in() ) { + if ( null === $loginUrl ) { + return ''; + } + + return sprintf( + '', + esc_url( $loginUrl ), + esc_html__( 'Sign in', 'unsupervised-schedular' ) + ); + } + + // Always a WP_User here — is_user_logged_in() above rules out the + // id-0 placeholder wp_get_current_user() returns for a visitor. + $user = wp_get_current_user(); + + $name = UserName::format( $user, get_current_user_id() ); + $email = $user->user_email; + + // Whose lessons this account books, when that is more than just their own. + // A parent's first question on seeing "signed in as Grace" is whether this + // is the account their children's lessons are on. + $students = []; + foreach ( $this->guardians->bookableStudents( get_current_user_id() ) as $student ) { + if ( ! $student['is_self'] ) { + $students[] = $student['name']; + } + } + + // Back to where they were, so signing out of a header link does not also + // navigate them somewhere. The login page is the better landing spot when + // one is configured, since the current page may be members-only. + $logoutUrl = wp_logout_url( $loginUrl ?? (string) get_permalink() ); + + ob_start(); + include USC_PLUGIN_DIR . 'templates/frontend/account-page.php'; + return (string) ob_get_clean(); + } + + /** + * Permalink of a configured page, or null when none is chosen or the chosen + * page has since been deleted. + */ + private function pageUrl( int $pageId ): ?string { + if ( $pageId <= 0 ) { + return null; + } + + $url = get_permalink( $pageId ); + + return is_string( $url ) ? $url : null; + } +} diff --git a/src/BlockPreview.php b/src/BlockPreview.php index 47bce0d..9f5fd1d 100644 --- a/src/BlockPreview.php +++ b/src/BlockPreview.php @@ -210,6 +210,32 @@ class BlockPreview { ); } + /** + * Sample account panel. Shown populated whatever the editor's own login + * state, since on the published page a signed-out visitor may see nothing at + * all and an empty box tells the person placing the block nothing. + */ + public static function account(): string { + return sprintf( + '', + self::note( __( 'Editor preview — each visitor sees their own account here.', 'unsupervised-schedular' ) ), + esc_html__( 'Grace Hopper', 'unsupervised-schedular' ), + esc_html__( 'grace@example.com', 'unsupervised-schedular' ), + esc_html( + sprintf( + /* translators: %s: comma-separated list of the students this account books for. */ + __( 'Booking for %s', 'unsupervised-schedular' ), + __( 'Ada, Alan', 'unsupervised-schedular' ) + ) + ), + esc_html__( 'Sign out', 'unsupervised-schedular' ) + ); + } + private static function note( string $text ): string { return '

' . esc_html( $text ) . '

'; } diff --git a/src/BlockRegistrar.php b/src/BlockRegistrar.php index f97c3c6..c59e1ff 100644 --- a/src/BlockRegistrar.php +++ b/src/BlockRegistrar.php @@ -3,6 +3,7 @@ declare(strict_types=1); namespace Unsupervised\Schedular; +use Unsupervised\Schedular\Auth\AccountPage; use Unsupervised\Schedular\Auth\LoginPage; use Unsupervised\Schedular\Auth\RegistrationPage; use Unsupervised\Schedular\Booking\BookingPage; @@ -30,6 +31,7 @@ class BlockRegistrar { private RegistrationPage $registrationPage, private GroupClassPage $groupClassPage, private FamilyPage $familyPage, + private AccountPage $accountPage, ) {} public function register(): void { @@ -148,6 +150,15 @@ class BlockRegistrar { ], ], ], + 'us-scheduler/account' => [ + 'render' => [ $this, 'renderAccount' ], + 'attributes' => [ + 'loginPageId' => [ + 'type' => 'number', + 'default' => 0, + ], + ], + ], ]; } @@ -195,6 +206,15 @@ class BlockRegistrar { return BlockPreview::groupClasses( Val::int( $attributes['offeringId'] ?? 0 ) > 0 ); } + /** + * Renders the account (who is signed in) block. + * + * @param array $attributes Block attributes. + */ + public function renderAccount( array $attributes = [] ): string { + return $this->isEditorPreview() ? BlockPreview::account() : $this->accountPage->render( $attributes ); + } + /** * Renders the family (manage-children) block. * diff --git a/src/Plugin.php b/src/Plugin.php index 889b966..fa65c95 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -5,6 +5,7 @@ namespace Unsupervised\Schedular; use Unsupervised\Schedular\Auth\EmailConfirmationHandler; use Unsupervised\Schedular\Auth\InviteRepository; +use Unsupervised\Schedular\Auth\AccountPage; use Unsupervised\Schedular\Auth\LoginPage; use Unsupervised\Schedular\Auth\RegistrationLoginGate; use Unsupervised\Schedular\Auth\RegistrationMailer; @@ -99,6 +100,7 @@ class Plugin { $registrationPage = new RegistrationPage( $invites, $policies, $policyVersions, $acceptances, $settings, $registrationMailer, $questions, $answers, $groupAccess, $guardians ); $groupClassPage = new GroupClassPage( $guardians ); $familyPage = new FamilyPage( $guardians, $questions, $answers ); + $accountPage = new AccountPage( $guardians ); ( new ScheduledBillingRunner( $paymentService, $bookings, $enrollments, $offerings, new PaymentDueMailer(), $guardians ) )->register(); @@ -110,7 +112,7 @@ class Plugin { ( new EmailConfirmationHandler( $settings, $registrationMailer ) )->register(); ( new AdminMenu( $availability, $bookings, $offerings, $questions, $answers, $policies, $policyVersions, $policyService, $acceptances, $invites, $enrollments, $groupAccess, $settings, $paymentRepo, $paymentService, $resolver, $registrationMailer, $creditRepo, $guardians ) )->register(); ( new RestRegistrar( $availability, $bookings, $offerings, $questions, $policies, $policyVersions, $policyService, $registrationGate, $enrollments, $groupAccess, $paymentService, $guardians ) )->register(); - ( new ShortcodeRegistrar( $bookingPage, $loginPage, $registrationPage, $groupClassPage, $familyPage ) )->register(); - ( new BlockRegistrar( $bookingPage, $loginPage, $registrationPage, $groupClassPage, $familyPage ) )->register(); + ( new ShortcodeRegistrar( $bookingPage, $loginPage, $registrationPage, $groupClassPage, $familyPage, $accountPage ) )->register(); + ( new BlockRegistrar( $bookingPage, $loginPage, $registrationPage, $groupClassPage, $familyPage, $accountPage ) )->register(); } } diff --git a/src/ShortcodeRegistrar.php b/src/ShortcodeRegistrar.php index 42d713c..977af61 100644 --- a/src/ShortcodeRegistrar.php +++ b/src/ShortcodeRegistrar.php @@ -3,6 +3,7 @@ declare(strict_types=1); namespace Unsupervised\Schedular; +use Unsupervised\Schedular\Auth\AccountPage; use Unsupervised\Schedular\Auth\LoginPage; use Unsupervised\Schedular\Auth\RegistrationPage; use Unsupervised\Schedular\Booking\BookingPage; @@ -18,6 +19,7 @@ class ShortcodeRegistrar { private RegistrationPage $registrationPage, private GroupClassPage $groupClassPage, private FamilyPage $familyPage, + private AccountPage $accountPage, ) {} public function register(): void { @@ -26,6 +28,7 @@ class ShortcodeRegistrar { add_shortcode( 'us_student_register', self::shortcode( [ $this->registrationPage, 'render' ] ) ); add_shortcode( 'us_group_classes', self::shortcode( [ $this->groupClassPage, 'render' ] ) ); add_shortcode( 'us_family', self::shortcode( [ $this->familyPage, 'render' ] ) ); + add_shortcode( 'us_account', self::shortcode( [ $this->accountPage, 'render' ] ) ); // Process registration submissions before output so the invite branch's // auth cookie is actually sent (render() runs too late, during the_content). add_action( 'template_redirect', [ $this->registrationPage, 'maybeHandleSubmit' ] ); diff --git a/templates/frontend/account-page.php b/templates/frontend/account-page.php new file mode 100644 index 0000000..3dcd446 --- /dev/null +++ b/templates/frontend/account-page.php @@ -0,0 +1,38 @@ + $students Names this account books for, excluding themselves; empty for a plain student account. + * @var string $logoutUrl Nonced sign-out URL, already carrying its redirect. + */ +?> + diff --git a/tests/Unit/Auth/AccountPageTest.php b/tests/Unit/Auth/AccountPageTest.php new file mode 100644 index 0000000..b1a672c --- /dev/null +++ b/tests/Unit/Auth/AccountPageTest.php @@ -0,0 +1,158 @@ +guardians = Mockery::mock(GuardianService::class); + $this->page = new AccountPage($this->guardians); + + Functions\when('is_user_logged_in')->justReturn(true); + Functions\when('get_current_user_id')->justReturn(5); + Functions\when('wp_enqueue_style')->justReturn(null); + Functions\when('get_permalink')->alias( + static fn (int $id = 0): string => $id > 0 + ? 'https://studio.test/sign-in/' + : 'https://studio.test/current/' + ); + Functions\when('wp_logout_url')->alias( + static fn (string $redirect): string => 'https://studio.test/wp-login.php?action=logout&redirect_to=' . rawurlencode($redirect) + ); + Functions\when('wp_get_current_user')->justReturn($this->user('Grace', 'Hopper', 'grace@studio.test')); + } + + private function user(string $first, string $last, string $email): \WP_User + { + $user = Mockery::mock(\WP_User::class); + $user->ID = 5; + $user->first_name = $first; + $user->last_name = $last; + $user->nickname = ''; + $user->user_email = $email; + + return $user; + } + + /** @param list $students */ + private function bookable(array $students): void + { + $this->guardians->shouldReceive('bookableStudents')->with(5)->andReturn($students); + } + + public function testShowsTheSignedInNameAndEmail(): void + { + $this->bookable([['id' => 5, 'name' => 'Grace Hopper', 'is_self' => true]]); + + $html = $this->page->render([]); + + self::assertStringContainsString('Grace Hopper', $html); + self::assertStringContainsString('grace@studio.test', $html); + self::assertStringContainsString('Sign out', $html); + } + + public function testNamesTheStudentsTheAccountBooksFor(): void + { + $this->bookable([ + ['id' => 42, 'name' => 'Ada', 'is_self' => false], + ['id' => 43, 'name' => 'Alan', 'is_self' => false], + ['id' => 5, 'name' => 'Grace Hopper', 'is_self' => true], + ]); + + self::assertStringContainsString('Booking for Ada, Alan', $this->page->render([])); + } + + /** + * An account that only books for itself has nothing to add — "Booking for + * Grace Hopper" under "Grace Hopper" is noise. + */ + public function testSaysNothingAboutStudentsOnAPlainAccount(): void + { + $this->bookable([['id' => 5, 'name' => 'Grace Hopper', 'is_self' => true]]); + + self::assertStringNotContainsString('Booking for', $this->page->render([])); + } + + public function testSigningOutReturnsToTheConfiguredLoginPage(): void + { + $this->bookable([['id' => 5, 'name' => 'Grace Hopper', 'is_self' => true]]); + + self::assertStringContainsString( + rawurlencode('https://studio.test/sign-in/'), + $this->page->render(['loginPageId' => 9]) + ); + } + + /** + * With no page chosen, signing out from a header link should leave the + * visitor where they were rather than navigating them somewhere. + */ + public function testSigningOutReturnsToTheCurrentPageWhenNoLoginPageIsSet(): void + { + $this->bookable([['id' => 5, 'name' => 'Grace Hopper', 'is_self' => true]]); + + self::assertStringContainsString( + rawurlencode('https://studio.test/current/'), + $this->page->render([]) + ); + } + + public function testTheShortcodeAttributeNameIsAccepted(): void + { + $this->bookable([['id' => 5, 'name' => 'Grace Hopper', 'is_self' => true]]); + + self::assertStringContainsString( + rawurlencode('https://studio.test/sign-in/'), + $this->page->render(['login_page_id' => 9]) + ); + } + + /** + * A block whose whole job is "you are signed in as X" has nothing to say to + * a stranger, and a bare notice in a site header cannot be acted on. + */ + public function testRendersNothingForASignedOutVisitorWithNoLoginPage(): void + { + Functions\when('is_user_logged_in')->justReturn(false); + $this->guardians->shouldNotReceive('bookableStudents'); + + self::assertSame('', $this->page->render([])); + } + + public function testOffersASignInLinkToASignedOutVisitorWhenAPageIsChosen(): void + { + Functions\when('is_user_logged_in')->justReturn(false); + $this->guardians->shouldNotReceive('bookableStudents'); + + $html = $this->page->render(['loginPageId' => 9]); + + self::assertStringContainsString('https://studio.test/sign-in/', $html); + self::assertStringContainsString('Sign in', $html); + self::assertStringNotContainsString('Sign out', $html); + } + + /** + * A page can be deleted after it has been chosen in the block, which + * get_permalink() reports as false. + */ + public function testTreatsADeletedLoginPageAsNoneChosen(): void + { + Functions\when('is_user_logged_in')->justReturn(false); + Functions\when('get_permalink')->justReturn(false); + + self::assertSame('', $this->page->render(['loginPageId' => 9])); + } +} diff --git a/tests/Unit/BlockRegistrarTest.php b/tests/Unit/BlockRegistrarTest.php index 9401247..3084a6b 100644 --- a/tests/Unit/BlockRegistrarTest.php +++ b/tests/Unit/BlockRegistrarTest.php @@ -6,6 +6,7 @@ namespace Unsupervised\Schedular\Tests\Unit; use Brain\Monkey\Actions; use Brain\Monkey\Functions; use Mockery; +use Unsupervised\Schedular\Auth\AccountPage; use Unsupervised\Schedular\Auth\LoginPage; use Unsupervised\Schedular\Auth\RegistrationPage; use Unsupervised\Schedular\BlockRegistrar; @@ -43,6 +44,7 @@ class BlockRegistrarTest extends TestCase private RegistrationPage&Mockery\MockInterface $registrationPage; private GroupClassPage&Mockery\MockInterface $groupClassPage; private FamilyPage&Mockery\MockInterface $familyPage; + private AccountPage&Mockery\MockInterface $accountPage; private TestableBlockRegistrar $registrar; protected function setUp(): void @@ -54,6 +56,7 @@ class BlockRegistrarTest extends TestCase $this->registrationPage = Mockery::mock(RegistrationPage::class); $this->groupClassPage = Mockery::mock(GroupClassPage::class); $this->familyPage = Mockery::mock(FamilyPage::class); + $this->accountPage = Mockery::mock(AccountPage::class); // Most requests are not a just-finished registration; the tests that // exercise that path override this. @@ -67,6 +70,7 @@ class BlockRegistrarTest extends TestCase $this->registrationPage, $this->groupClassPage, $this->familyPage, + $this->accountPage, ); } @@ -116,6 +120,7 @@ class BlockRegistrarTest extends TestCase 'us-scheduler/student-register', 'us-scheduler/group-classes', 'us-scheduler/family', + 'us-scheduler/account', ], array_keys($registered) ); @@ -213,6 +218,7 @@ class BlockRegistrarTest extends TestCase $this->registrationPage, $this->groupClassPage, $this->familyPage, + $this->accountPage, ); $this->bookingPage->shouldReceive('render')->once()->with([])->andReturn('live'); diff --git a/tests/Unit/ShortcodeRegistrarTest.php b/tests/Unit/ShortcodeRegistrarTest.php index 790ff17..726a9cf 100644 --- a/tests/Unit/ShortcodeRegistrarTest.php +++ b/tests/Unit/ShortcodeRegistrarTest.php @@ -6,6 +6,7 @@ namespace Unsupervised\Schedular\Tests\Unit; use Brain\Monkey\Actions; use Brain\Monkey\Functions; use Mockery; +use Unsupervised\Schedular\Auth\AccountPage; use Unsupervised\Schedular\Auth\LoginPage; use Unsupervised\Schedular\Auth\RegistrationPage; use Unsupervised\Schedular\Booking\BookingPage; @@ -20,6 +21,7 @@ class ShortcodeRegistrarTest extends TestCase private RegistrationPage&Mockery\MockInterface $registrationPage; private GroupClassPage&Mockery\MockInterface $groupClassPage; private FamilyPage&Mockery\MockInterface $familyPage; + private AccountPage&Mockery\MockInterface $accountPage; private ShortcodeRegistrar $registrar; /** @var array */ @@ -37,6 +39,7 @@ class ShortcodeRegistrarTest extends TestCase $this->registrationPage = Mockery::mock(RegistrationPage::class); $this->groupClassPage = Mockery::mock(GroupClassPage::class); $this->familyPage = Mockery::mock(FamilyPage::class); + $this->accountPage = Mockery::mock(AccountPage::class); $this->registrar = new ShortcodeRegistrar( $this->bookingPage, @@ -44,6 +47,7 @@ class ShortcodeRegistrarTest extends TestCase $this->registrationPage, $this->groupClassPage, $this->familyPage, + $this->accountPage, ); $shortcodes = &$this->shortcodes; @@ -66,7 +70,7 @@ class ShortcodeRegistrarTest extends TestCase $this->registrar->register(); self::assertSame( - ['us_booking', 'us_student_login', 'us_student_register', 'us_group_classes', 'us_family'], + ['us_booking', 'us_student_login', 'us_student_register', 'us_group_classes', 'us_family', 'us_account'], array_keys($this->shortcodes) ); }