diff --git a/src/resources/js/app.js b/src/resources/js/app.js index 11b98fd9..88a76180 100644 --- a/src/resources/js/app.js +++ b/src/resources/js/app.js @@ -1,427 +1,429 @@ /** * First we will load all of this project's JavaScript dependencies which * includes Vue and other libraries. It is a great starting point when * building robust, powerful web applications using Vue and Laravel. */ require('./bootstrap') import AppComponent from '../vue/App' import MenuComponent from '../vue/Widgets/Menu' import SupportForm from '../vue/Widgets/SupportForm' import store from './store' const loader = '
') ->click('@setup-button') ->assertMissing('@setup-nickname-input.is-invalid') - ->waitFor('@setup-button[disabled]') - ->assertSeeIn('@setup-status-message', "Waiting for permission to join the room."); + ->waitForText("Waiting for permission to join the room.") + ->assertButtonDisabled('@setup-button'); // Test denying the request (this will also test custom toasts) $owner ->whenAvailable(new Toast(Toast::TYPE_CUSTOM), function ($browser) { $browser->assertToastTitle('Join request') ->assertVisible('.toast-header svg.fa-user') ->assertSeeIn('@message', 'Guest
requested to join.') ->assertAttributeRegExp('@message img', 'src', '|^data:image|') ->assertSeeIn('@message button.accept.btn-success', 'Accept') ->assertSeeIn('@message button.deny.btn-danger', 'Deny') ->click('@message button.deny'); }) ->waitUntilMissing('.toast') // wait 10 seconds to make sure the request message does not show up again ->pause(10 * 1000) ->assertMissing('.toast'); // Test accepting the request $guest->refresh() ->waitFor('@setup-form') ->waitUntilMissing('@setup-status-message.loading') ->type('@setup-nickname-input', 'guest') ->click('@setup-button') - ->waitFor('@setup-button[disabled]') - ->assertSeeIn('@setup-status-message', "Waiting for permission to join the room."); + ->waitForText("Waiting for permission to join the room.") + ->assertButtonDisabled('@setup-button'); $owner ->whenAvailable(new Toast(Toast::TYPE_CUSTOM), function ($browser) { $browser->assertToastTitle('Join request') ->assertSeeIn('@message', 'guest requested to join.') ->click('@message button.accept'); }); // Guest automatically anters the room $guest->waitFor('@session', 12) // make sure he has no access to the Options menu ->waitFor('@session .meet-video:not(.self)') ->assertSeeIn('@session .meet-video:not(.self) .meet-nickname', 'John') // TODO: Assert title and icon ->click('@session .meet-video:not(.self) .meet-nickname') ->pause(100) ->assertMissing('.dropdown-menu'); // Test dismissing the participant $owner->click('@session .meet-video:not(.self) .meet-nickname') ->waitFor('@session .meet-video:not(.self) .dropdown-menu') ->assertSeeIn('@session .meet-video:not(.self) .dropdown-menu > .action-dismiss', 'Dismiss') ->click('@session .meet-video:not(.self) .dropdown-menu > .action-dismiss') ->waitUntilMissing('.dropdown-menu') ->waitUntilMissing('@session .meet-video:not(.self)'); // Expect a "end of session" dialog on the participant side $guest->with(new Dialog('#leave-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', 'Room closed') ->assertSeeIn('@body', "The session has been closed by the room owner.") ->assertMissing('@button-cancel') ->assertSeeIn('@button-action', 'Close'); }); }); } } diff --git a/src/tests/Browser/Meet/RoomSetupTest.php b/src/tests/Browser/Meet/RoomSetupTest.php index 7e98a21f..fc9881d6 100644 --- a/src/tests/Browser/Meet/RoomSetupTest.php +++ b/src/tests/Browser/Meet/RoomSetupTest.php @@ -1,370 +1,373 @@ clearBetaEntitlements(); } public function tearDown(): void { $this->clearBetaEntitlements(); parent::tearDown(); } /** * Test non-existing room * * @group openvidu */ public function testRoomNonExistingRoom(): void { $this->browse(function (Browser $browser) { $browser->visit(new RoomPage('unknown')) ->within(new Menu(), function ($browser) { $browser->assertMenuItems(['signup', 'explore', 'blog', 'support', 'login']); }); if ($browser->isDesktop()) { $browser->within(new Menu('footer'), function ($browser) { $browser->assertMenuItems(['signup', 'explore', 'blog', 'support', 'tos', 'login']); }); } else { $browser->assertMissing('#footer-menu .navbar-nav'); } // FIXME: Maybe it would be better to just display the usual 404 Not Found error page? $browser->assertMissing('@toolbar') ->assertMissing('@menu') ->assertMissing('@session') ->assertMissing('@chat') ->assertMissing('@login-form') ->assertVisible('@setup-form') ->assertSeeIn('@setup-status-message', "The room does not exist.") - ->assertVisible('@setup-button[disabled]'); + ->assertButtonDisabled('@setup-button'); }); } /** * Test the room setup page * * @group openvidu */ public function testRoomSetup(): void { // Make sure there's no session yet $room = Room::where('name', 'john')->first(); if ($room->session_id) { $room->session_id = null; $room->save(); } $this->assignBetaEntitlement('john@kolab.org', 'meet'); $this->browse(function (Browser $browser) { $browser->visit(new RoomPage('john')) ->within(new Menu(), function ($browser) { $browser->assertMenuItems(['signup', 'explore', 'blog', 'support', 'login']); }); if ($browser->isDesktop()) { $browser->within(new Menu('footer'), function ($browser) { $browser->assertMenuItems(['signup', 'explore', 'blog', 'support', 'tos', 'login']); }); } else { $browser->assertMissing('#footer-menu .navbar-nav'); } // Note: I've found out that if I have another Chrome instance running // that uses media, here the media devices will not be available // TODO: Test enabling/disabling cam/mic in the setup widget $browser->assertMissing('@toolbar') ->assertMissing('@menu') ->assertMissing('@session') ->assertMissing('@chat') ->assertMissing('@login-form') ->assertVisible('@setup-form') ->assertSeeIn('@setup-title', 'Set up your session') ->assertVisible('@setup-video') ->assertVisible('@setup-form .input-group:nth-child(1) svg') ->assertAttribute('@setup-form .input-group:nth-child(1) .input-group-text', 'title', 'Microphone') ->assertVisible('@setup-mic-select') ->assertVisible('@setup-form .input-group:nth-child(2) svg') ->assertAttribute('@setup-form .input-group:nth-child(2) .input-group-text', 'title', 'Camera') ->assertVisible('@setup-cam-select') ->assertVisible('@setup-form .input-group:nth-child(3) svg') ->assertAttribute('@setup-form .input-group:nth-child(3) .input-group-text', 'title', 'Nickname') ->assertValue('@setup-nickname-input', '') ->assertAttribute('@setup-nickname-input', 'placeholder', 'Your name') ->assertMissing('@setup-password-input') ->assertSeeIn( '@setup-status-message', "The room is closed. Please, wait for the owner to start the session." ) ->assertSeeIn('@setup-button', "I'm the owner"); }); } /** * Test two users in a room (joining/leaving and some basic functionality) * * @group openvidu * @depends testRoomSetup */ public function testTwoUsersInARoom(): void { $this->assignBetaEntitlement('john@kolab.org', 'meet'); $this->browse(function (Browser $browser, Browser $guest) { // In one browser window act as a guest $guest->visit(new RoomPage('john')) ->assertMissing('@toolbar') ->assertMissing('@menu') ->assertMissing('@session') ->assertMissing('@chat') ->assertMissing('@login-form') ->waitFor('@setup-form') ->waitUntilMissing('@setup-status-message.loading') ->assertSeeIn( '@setup-status-message', "The room is closed. Please, wait for the owner to start the session." ) ->assertSeeIn('@setup-button', "I'm the owner"); // In another window join the room as the owner (authenticate) $browser->on(new RoomPage('john')) ->assertSeeIn('@setup-button', "I'm the owner") ->click('@setup-button') ->assertMissing('@toolbar') ->assertMissing('@menu') ->assertMissing('@session') ->assertMissing('@chat') ->assertMissing('@setup-form') ->assertVisible('@login-form') ->submitLogon('john@kolab.org', 'simple123') ->waitFor('@setup-form') ->assertMissing('@login-form') ->waitUntilMissing('@setup-status-message.loading') ->waitFor('@setup-status-message') ->assertSeeIn('@setup-status-message', "The room is closed. It will be open for others after you join.") ->assertSeeIn('@setup-button', "JOIN") ->type('@setup-nickname-input', 'john') - // Join the room + // Join the room (click the button twice, to make sure it does not + // produce redundant participants/subscribers in the room) + ->click('@setup-button') + ->pause(10) ->click('@setup-button') ->waitFor('@session') ->assertMissing('@setup-form') ->whenAvailable('div.meet-video.self', function (Browser $browser) { $browser->assertVisible('video') ->assertSeeIn('.meet-nickname', 'john') ->assertVisible('.controls button.link-fullscreen') ->assertMissing('.controls button.link-audio') ->assertMissing('.status .status-audio') ->assertMissing('.status .status-video'); }) ->within(new Menu(), function ($browser) { $browser->assertMenuItems(['explore', 'blog', 'support', 'dashboard', 'logout']); }); if ($browser->isDesktop()) { $browser->within(new Menu('footer'), function ($browser) { $browser->assertMenuItems(['explore', 'blog', 'support', 'tos', 'dashboard', 'logout']); }); } // After the owner "opened the room" guest should be able to join $guest->waitUntilMissing('@setup-status-message', 10) ->assertSeeIn('@setup-button', "JOIN") // Join the room, disable cam/mic ->select('@setup-mic-select', '') //->select('@setup-cam-select', '') ->click('@setup-button') ->waitFor('@session') ->assertMissing('@setup-form') ->whenAvailable('div.meet-video.self', function (Browser $browser) { $browser->assertVisible('video') ->assertVisible('.meet-nickname') ->assertVisible('.controls button.link-fullscreen') ->assertMissing('.controls button.link-audio') ->assertVisible('.status .status-audio') ->assertMissing('.status .status-video'); }) ->whenAvailable('div.meet-video:not(.self)', function (Browser $browser) { $browser->assertVisible('video') ->assertSeeIn('.meet-nickname', 'john') ->assertVisible('.controls button.link-fullscreen') ->assertVisible('.controls button.link-audio') ->assertMissing('.status .status-audio') ->assertMissing('.status .status-video'); }) ->assertElementsCount('@session div.meet-video', 2) ->within(new Menu(), function ($browser) { $browser->assertMenuItems(['explore', 'blog', 'support', 'signup', 'login']); }); if ($guest->isDesktop()) { $guest->within(new Menu('footer'), function ($browser) { $browser->assertMenuItems(['explore', 'blog', 'support', 'tos', 'signup', 'login']); }); } // Check guest's elements in the owner's window $browser ->whenAvailable('div.meet-video:not(.self)', function (Browser $browser) { $browser->assertVisible('video') ->assertVisible('.meet-nickname') ->assertVisible('.controls button.link-fullscreen') ->assertVisible('.controls button.link-audio') ->assertVisible('.status .status-audio') ->assertMissing('.status .status-video'); }) ->assertElementsCount('@session div.meet-video', 2); // Test leaving the room // Guest is leaving $guest->click('@menu button.link-logout') ->waitForLocation('/login'); // Expect the participant removed from other users windows $browser->waitUntilMissing('@session div.meet-video:not(.self)'); // Join the room as guest again $guest->visit(new RoomPage('john')) ->assertMissing('@toolbar') ->assertMissing('@menu') ->assertMissing('@session') ->assertMissing('@chat') ->assertMissing('@login-form') ->waitFor('@setup-form') ->waitUntilMissing('@setup-status-message.loading') ->assertMissing('@setup-status-message') ->assertSeeIn('@setup-button', "JOIN") // Join the room, disable cam/mic ->select('@setup-mic-select', '') //->select('@setup-cam-select', '') ->click('@setup-button') ->waitFor('@session'); // Leave the room as the room owner // TODO: Test leaving the room by closing the browser window, // it should not destroy the session $browser->click('@menu button.link-logout') ->waitForLocation('/dashboard'); // Expect other participants be informed about the end of the session $guest->with(new Dialog('#leave-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', 'Room closed') ->assertSeeIn('@body', "The session has been closed by the room owner.") ->assertMissing('@button-cancel') ->assertSeeIn('@button-action', 'Close') ->click('@button-action'); }) ->assertMissing('#leave-dialog') ->waitForLocation('/login'); }); } /** * Test two subscribers-only users in a room * * @group openvidu * @depends testTwoUsersInARoom */ public function testSubscribers(): void { $this->assignBetaEntitlement('john@kolab.org', 'meet'); $this->browse(function (Browser $browser, Browser $guest) { // Join the room as the owner $browser->visit(new RoomPage('john')) ->waitFor('@setup-form') ->waitUntilMissing('@setup-status-message.loading') ->waitFor('@setup-status-message') ->type('@setup-nickname-input', 'john') ->select('@setup-mic-select', '') ->select('@setup-cam-select', '') ->click('@setup-button') ->waitFor('@session') ->assertMissing('@setup-form') ->whenAvailable('@subscribers .meet-subscriber.self', function (Browser $browser) { $browser->assertSeeIn('.meet-nickname', 'john'); }) ->assertElementsCount('@session div.meet-video', 0) ->assertElementsCount('@session video', 0) ->assertElementsCount('@session .meet-subscriber', 1) ->assertToolbar([ 'audio' => RoomPage::BUTTON_INACTIVE | RoomPage::BUTTON_DISABLED, 'video' => RoomPage::BUTTON_INACTIVE | RoomPage::BUTTON_DISABLED, 'screen' => RoomPage::BUTTON_INACTIVE | RoomPage::BUTTON_DISABLED, 'chat' => RoomPage::BUTTON_INACTIVE | RoomPage::BUTTON_ENABLED, 'fullscreen' => RoomPage::BUTTON_ACTIVE | RoomPage::BUTTON_ENABLED, 'security' => RoomPage::BUTTON_ACTIVE | RoomPage::BUTTON_ENABLED, 'logout' => RoomPage::BUTTON_ACTIVE | RoomPage::BUTTON_ENABLED, ]); // After the owner "opened the room" guest should be able to join // In one browser window act as a guest $guest->visit(new RoomPage('john')) ->waitUntilMissing('@setup-status-message', 10) ->assertSeeIn('@setup-button', "JOIN") // Join the room, disable cam/mic ->select('@setup-mic-select', '') ->select('@setup-cam-select', '') ->click('@setup-button') ->waitFor('@session') ->assertMissing('@setup-form') ->whenAvailable('@subscribers .meet-subscriber.self', function (Browser $browser) { $browser->assertVisible('.meet-nickname'); }) ->whenAvailable('@subscribers .meet-subscriber:not(.self)', function (Browser $browser) { $browser->assertSeeIn('.meet-nickname', 'john'); }) ->assertElementsCount('@session div.meet-video', 0) ->assertElementsCount('@session video', 0) ->assertElementsCount('@session div.meet-subscriber', 2) ->assertToolbar([ 'audio' => RoomPage::BUTTON_INACTIVE | RoomPage::BUTTON_DISABLED, 'video' => RoomPage::BUTTON_INACTIVE | RoomPage::BUTTON_DISABLED, 'screen' => RoomPage::BUTTON_INACTIVE | RoomPage::BUTTON_DISABLED, 'chat' => RoomPage::BUTTON_INACTIVE | RoomPage::BUTTON_ENABLED, 'fullscreen' => RoomPage::BUTTON_ACTIVE | RoomPage::BUTTON_ENABLED, 'logout' => RoomPage::BUTTON_ACTIVE | RoomPage::BUTTON_ENABLED, ]); // Check guest's elements in the owner's window $browser ->whenAvailable('@subscribers .meet-subscriber:not(.self)', function (Browser $browser) { $browser->assertVisible('.meet-nickname'); }) ->assertElementsCount('@session div.meet-video', 0) ->assertElementsCount('@session video', 0) ->assertElementsCount('@session .meet-subscriber', 2); // Test leaving the room // Guest is leaving $guest->click('@menu button.link-logout') ->waitForLocation('/login'); // Expect the participant removed from other users windows $browser->waitUntilMissing('@session .meet-subscriber:not(.self)'); }); } } diff --git a/src/tests/Browser/Meet/RoomsTest.php b/src/tests/Browser/Meet/RoomsTest.php index 1dc99a57..b7e635e7 100644 --- a/src/tests/Browser/Meet/RoomsTest.php +++ b/src/tests/Browser/Meet/RoomsTest.php @@ -1,111 +1,111 @@ clearBetaEntitlements(); } /** * {@inheritDoc} */ public function tearDown(): void { $this->clearBetaEntitlements(); parent::tearDown(); } /** * Test rooms page (unauthenticated and unauthorized) * * @group openvidu */ public function testRoomsUnauth(): void { // Test that the page requires authentication $this->browse(function (Browser $browser) { $browser->visit('/rooms') ->on(new Home()) // User has no 'meet' entitlement yet, expect redirect to error page ->submitLogon('john@kolab.org', 'simple123', false) ->waitFor('#app > #error-page') ->assertSeeIn('#error-page .code', '403') ->assertSeeIn('#error-page .message', 'Access denied'); }); } /** * Test rooms page * * @group openvidu */ public function testRooms(): void { $this->browse(function (Browser $browser) { $href = \config('app.url') . '/meet/john'; $john = $this->getTestUser('john@kolab.org'); $john->assignSku(Sku::where('title', 'beta')->first()); // User has no 'meet' entitlement yet $browser->visit('/login') ->on(new Home()) ->submitLogon('john@kolab.org', 'simple123', true) ->on(new Dashboard()) ->assertMissing('@links a.link-chat'); // Goto user subscriptions, and enable 'meet' subscription $browser->visit('/user/' . $john->id) ->on(new UserInfo()) ->with('@skus', function ($browser) { $browser->click('#sku-input-meet'); }) ->click('button[type=submit]') ->assertToast(Toast::TYPE_SUCCESS, 'User data updated successfully.') ->click('.navbar-brand') ->on(new Dashboard()) ->assertSeeIn('@links a.link-chat', 'Video chat') // Make sure the element also exists on Dashboard page load ->refresh() ->on(new Dashboard()) ->assertSeeIn('@links a.link-chat', 'Video chat'); // Test Video chat page $browser->click('@links a.link-chat') ->waitFor('#meet-rooms') ->waitFor('.card-text a') - ->assertSeeIn('.card-title', 'Video chat') + ->assertSeeIn('.card-title', 'Voice & Video Conferencing') ->assertSeeIn('.card-text a', $href) ->assertAttribute('.card-text a', 'href', $href) ->click('.card-text a') ->on(new RoomPage('john')) // check that entering the room skips the logon form ->assertMissing('@toolbar') ->assertMissing('@menu') ->assertMissing('@session') ->assertMissing('@chat') ->assertMissing('@login-form') ->assertVisible('@setup-form') ->assertSeeIn('@setup-status-message', "The room is closed. It will be open for others after you join.") ->assertSeeIn('@setup-button', "JOIN") ->click('@setup-button') ->waitFor('@session') ->assertMissing('@setup-form'); }); } } diff --git a/src/tests/Browser/Pages/Meet/Room.php b/src/tests/Browser/Pages/Meet/Room.php index 498670a8..825da0be 100644 --- a/src/tests/Browser/Pages/Meet/Room.php +++ b/src/tests/Browser/Pages/Meet/Room.php @@ -1,200 +1,200 @@ roomName = $name; } /** * Get the URL for the page. * * @return string */ public function url() { return '/meet/' . $this->roomName; } /** * Assert that the browser is on the page. * * @param \Laravel\Dusk\Browser $browser The browser object * * @return void */ public function assert($browser) { $browser->waitForLocation($this->url()) ->waitUntilMissing('.app-loader') ->waitUntilMissing('#meet-setup div.status-message.loading'); } /** * Get the element shortcuts for the page. * * @return array */ public function elements() { return [ '@app' => '#app', '@setup-form' => '#meet-setup form', '@setup-title' => '#meet-setup .card-title', '@setup-mic-select' => '#setup-microphone', '@setup-cam-select' => '#setup-camera', '@setup-nickname-input' => '#setup-nickname', '@setup-password-input' => '#setup-password', '@setup-preview' => '#setup-preview', '@setup-volume' => '#setup-preview .volume', '@setup-video' => '#setup-preview video', '@setup-status-message' => '#meet-setup div.status-message', - '@setup-button' => '#meet-setup form button', + '@setup-button' => '#join-button', '@toolbar' => '#meet-session-toolbar', '@menu' => '#meet-session-menu', '@session' => '#meet-session', '@subscribers' => '#meet-subscribers', '@chat' => '#meet-chat', '@chat-input' => '#meet-chat textarea', '@chat-list' => '#meet-chat .chat', '@login-form' => '#meet-auth', '@login-email-input' => '#inputEmail', '@login-password-input' => '#inputPassword', '@login-second-factor-input' => '#secondfactor', '@login-button' => '#meet-auth button', ]; } /** * Assert menu state. * * @param \Tests\Browser $browser The browser object * @param array $menu Menu items/state */ public function assertToolbar($browser, array $menu): void { $browser->assertElementsCount('@menu button', count($menu)); foreach ($menu as $item => $state) { $this->assertToolbarButtonState($browser, $item, $state); } } /** * Assert menu button state. * * @param \Tests\Browser $browser The browser object * @param string $button Button name * @param int $state Expected button state (sum of BUTTON_* consts) */ public function assertToolbarButtonState($browser, $button, $state): void { $class = ''; if ($state & self::BUTTON_ACTIVE) { $class .= ':not(.text-danger)'; } if ($state & self::BUTTON_INACTIVE) { $class .= '.text-danger'; } if ($state & self::BUTTON_DISABLED) { $class .= '[disabled]'; } if ($state & self::BUTTON_ENABLED) { $class .= ':not([disabled])'; } $browser->assertVisible('@menu button.link-' . $button . $class); } /** * Assert the