create([ 'name' => 'TU Test User', 'email' => 'test-owner@example.com', 'password' => bcrypt('password'), ]); $participant = User::factory()->create([ 'name' => 'Submissive Bob', 'email' => 'test-sub@example.com', 'password' => bcrypt('password'), ]); $dynamic = Dynamic::create([ 'name' => 'The Test Sanctuary', 'rules' => 'Rules for realtime testing.', ]); $dynamic->participants()->attach($owner->id, ['role' => 'owner']); $dynamic->participants()->attach($participant->id, ['role' => 'participant']); // 2. Spawn two separate browser sessions/browsers in parallel $this->browse(function (Browser $sessionA, Browser $sessionB) use ($dynamic, $owner, $participant) { // --- SESSION A: Owner --- $sessionA->loginAs($owner) ->visit(route('dynamics.show', $dynamic)) ->waitForText('The Test Sanctuary') ->assertSee('TU Test User'); // Verify loaded in as Owner // --- SESSION B: Participant --- $sessionB->loginAs($participant) ->visit(route('dynamics.show', $dynamic)) ->waitForText('The Test Sanctuary') ->assertSee('Submissive Bob'); // Verify loaded in as Submissive/Participant // --- REAL-TIME COMMUNICATING --- // Owner types and sends a message in chat $sessionA->type('#content', 'Hello Submissive Bob, did you complete your daily chores?') ->click('.c-chat__button') ->waitForText('Hello Submissive Bob'); // Since websockets broadcast in real-time, Session B receives it without reloading $sessionB->waitForText('Hello Submissive Bob', 5) ->assertSee('Hello Submissive Bob, did you complete your daily chores?'); // Participant replies in real-time $sessionB->type('#content', 'Yes Master, everything is complete and logged in the ledger!') ->click('.c-chat__button') ->waitForText('Yes Master, everything is complete'); // Session A receives the reply in real-time without reloading $sessionA->waitForText('Yes Master, everything is complete', 5) ->assertSee('Yes Master, everything is complete and logged in the ledger!'); }); });