Skip to content
This repository was archived by the owner on Oct 20, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/.env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
APP_NAME=Laravel
APP_NAME="Laravel Splade"
APP_ENV=local
APP_KEY=base64:TBVjDAO1zLKHWGrb6ZI3JWSoJgUWkB8Wf6GfQdes4h8=
APP_DEBUG=true
Expand Down
39 changes: 39 additions & 0 deletions app/app/Http/Controllers/ModalController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace App\Http\Controllers;

use ProtoneMedia\Splade\Facades\SEO;

class ModalController
{
public function base()
{
SEO::title('Modal Base');

return view('modal.base');
}

public function one()
{
SEO::title('Modal One');

return view('modal.one');
}

public function two()
{
SEO::title('Modal Two');

return view('modal.two');
}

public function slideover()
{
return view('modal.slideover');
}

public function validation()
{
return view('modal.validation');
}
}
36 changes: 36 additions & 0 deletions app/app/Http/Controllers/NavigationController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace App\Http\Controllers;

use ProtoneMedia\Splade\Facades\SEO;

class NavigationController
{
public function one()
{
SEO::title('Navigation One')
->description('First Navigation')
->keywords('een, one');

return view('navigation.one');
}

public function two()
{
SEO::title('Navigation Two')
->description('Second Navigation')
->keywords(['twee', 'two']);

return view('navigation.two');
}

public function three()
{
return view('navigation.three');
}

public function form()
{
return view('navigation.form');
}
}
3 changes: 1 addition & 2 deletions app/resources/views/root.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

<title>Laravel</title>

<!-- Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap" rel="stylesheet">

@spladeHead
@vite('resources/js/app.js')
</head>
<body class="antialiased">
Expand Down
21 changes: 12 additions & 9 deletions app/routes/web.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
use App\Events\ToastEvent;
use App\Http\Controllers\BackFormController;
use App\Http\Controllers\FileFormController;
use App\Http\Controllers\ModalController;
use App\Http\Controllers\NavigationController;
use App\Http\Controllers\SimpleFormController;
use App\Http\Controllers\SlowFormController;
use App\Http\Controllers\ToastController;
Expand Down Expand Up @@ -69,18 +71,19 @@
Route::view('form/jsonable', 'form.jsonable')->name('form.jsonable');
Route::view('form/jsonSerializable', 'form.jsonSerializable')->name('form.jsonSerializable');

Route::view('navigation/one', 'navigation.one')->name('navigation.one');
Route::view('navigation/two', 'navigation.two')->name('navigation.two');
Route::view('navigation/three', 'navigation.three')->name('navigation.three');
Route::view('navigation/form', 'navigation.form')->name('navigation.form');
Route::get('navigation/one', [NavigationController::class, 'one'])->name('navigation.one');
Route::get('navigation/two', [NavigationController::class, 'two'])->name('navigation.two');
Route::get('navigation/three', [NavigationController::class, 'three'])->name('navigation.three');
Route::get('navigation/form', [NavigationController::class, 'form'])->name('navigation.form');

Route::get('navigation/notFound', fn () => abort(404))->name('navigation.notFound');
Route::get('navigation/serverError', fn () => throw new Exception('Whoops!'))->name('navigation.serverError');

Route::view('modal/base', 'modal.base')->name('modal.base');
Route::view('modal/one', 'modal.one')->name('modal.one');
Route::view('modal/two', 'modal.two')->name('modal.two');
Route::view('modal/slideover', 'modal.slideover')->name('modal.slideover');
Route::view('modal/validation', 'modal.validation')->name('modal.validation');
Route::get('modal/base', [ModalController::class, 'base'])->name('modal.base');
Route::get('modal/one', [ModalController::class, 'one'])->name('modal.one');
Route::get('modal/two', [ModalController::class, 'two'])->name('modal.two');
Route::get('modal/slideover', [ModalController::class, 'slideover'])->name('modal.slideover');
Route::get('modal/validation', [ModalController::class, 'validation'])->name('modal.validation');

Route::post('state', function () {
Splade::share('info', 'This is invalid');
Expand Down
22 changes: 22 additions & 0 deletions app/tests/Browser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace Tests;

use Laravel\Dusk\Browser as BaseBrowser;
use PHPUnit\Framework\Assert as PHPUnit;

class Browser extends BaseBrowser
{
public function assertMetaByName($name, $content)
{
$driverContent = $this->driver->executeScript('return document.querySelector("meta[name=\"' . $name . '\"]")?.getAttribute("content")');

PHPUnit::assertEquals(
$content,
$driverContent,
"Meta with name [{$name}] expected content [{$content}] does not equal actual title [{$driverContent}]."
);

return $this;
}
}
58 changes: 58 additions & 0 deletions app/tests/Browser/HeadTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

namespace Tests\Browser;

use Tests\Browser;
use Tests\DuskTestCase;

class HeadTest extends DuskTestCase
{
/** @test */
public function it_updates_the_title_and_meta_tags()
{
$this->browse(function (Browser $browser) {
$browser->visit('/navigation/one')
->waitForText('NavigationOne')
->assertTitle('Navigation One')
->assertMetaByName('description', 'First Navigation')
->assertMetaByName('keywords', 'een, one')
->click('@two')
->waitForText('NavigationTwo')
->assertTitle('Navigation Two')
->assertMetaByName('description', 'Second Navigation')
->assertMetaByName('keywords', 'twee, two')
->click('@three')
->waitForText('NavigationThree')

// defaults:
->assertTitle('Laravel Splade')
->assertMetaByName('description', 'Splade provides a super easy way to build Single Page Applications (SPA) using standard Laravel Blade templates, enhanced with renderless Vue 3 components.')
->assertMetaByName('keywords', 'Laravel, Splade');
});
}

/** @test */
public function it_updates_the_head_when_modals_are_opened_and_closed()
{
$this->browse(function (Browser $browser) {
$browser->visit('/modal/base')
->waitForText('ModalComponent')
->assertTitle('Modal Base')
->click('@one')
->waitForText('ModalComponentOne')
->pause(500)
->assertTitle('Modal One')
->click('@two')
->waitForText('ModalComponentTwo')
->pause(500)
->assertTitle('Modal Two')
->click('@close-two')
->waitForText('ModalComponentOne')
->pause(500)
->assertTitle('Modal One')
->click('@close-one')
->pause(500)
->assertTitle('Modal Base');
});
}
}
12 changes: 11 additions & 1 deletion app/tests/DuskTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Illuminate\Support\Arr;
use Laravel\Dusk\Browser;
use Laravel\Dusk\TestCase as BaseTestCase;
use Spatie\Snapshots\MatchesSnapshots;

Expand All @@ -15,6 +14,17 @@ abstract class DuskTestCase extends BaseTestCase
use CreatesApplication;
use MatchesSnapshots;

/**
* Create a new Browser instance.
*
* @param \Facebook\WebDriver\Remote\RemoteWebDriver $driver
* @return \Tests\Browser
*/
protected function newBrowser($driver)
{
return new Browser($driver);
}

/**
* Prepare for Dusk test execution.
*
Expand Down
17 changes: 17 additions & 0 deletions app/tests/Feature/HeadTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Tests\Feature;

use Tests\TestCase;

class HeadTest extends TestCase
{
/** @test */
public function it_renders_the_head()
{
$this->get('/navigation/one')
->assertSee('<title>Navigation One</title>', false)
->assertSee('<meta name="description" content="First Navigation" />', false)
->assertSee('<meta name="keywords" content="een, one" />', false);
}
}
4 changes: 2 additions & 2 deletions app/tests/Feature/SsrTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ public function it_has_a_server_that_handles_ssr_requests()
$this->assertArrayHasKey('body', $data);

// evaluated form
$this->assertStringContainsString('<form><input dusk="name" value="Splade"></form>', $data['body']);
$this->assertStringContainsString('<form><input dusk="name" value="Splade"></form>', $data['body'] ?? '');

// rendered components
$this->assertStringContainsString('grid grid-cols-3 grid-flow-row-3', $data['body']);
$this->assertStringContainsString('grid grid-cols-3 grid-flow-row-3', $data['body'] ?? '');
}
}
8 changes: 3 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@
"laravel/pint": "^1.0",
"nunomaduro/collision": "^6.0",
"nunomaduro/larastan": "^2.0.1",
"orchestra/testbench": "^7.0",
"pestphp/pest": "^1.21",
"pestphp/pest-plugin-laravel": "^1.1",
"phpunit/phpunit": "^9.5"
},
"autoload": {
Expand Down Expand Up @@ -57,8 +54,9 @@
"ProtoneMedia\\Splade\\ServiceProvider"
],
"aliases": {
"Toast": "ProtoneMedia\\Splade\\Facades\\Toast",
"Splade": "ProtoneMedia\\Splade\\Facades\\Splade"
"SEO": "ProtoneMedia\\Splade\\Facades\\SEO",
"Splade": "ProtoneMedia\\Splade\\Facades\\Splade",
"Toast": "ProtoneMedia\\Splade\\Facades\\Toast"
}
}
},
Expand Down
18 changes: 18 additions & 0 deletions config/splade.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,24 @@
'component_prefix' => 'splade',
],

'seo' => [

'title_prefix' => '',

'title_suffix' => '',

'defaults' => [

'title' => env('APP_NAME', 'Laravel Splade'),

'description' => 'Splade provides a super easy way to build Single Page Applications (SPA) using standard Laravel Blade templates, enhanced with renderless Vue 3 components.',

'keywords' => ['Laravel', 'Splade'],

],

],

'ssr' => [

'enabled' => env('SPLADE_SSR_ENABLED', false),
Expand Down
Loading