Merge branch 'feature/dusk' into develop

This commit is contained in:
Jonny Barnes 2017-02-26 17:21:55 +00:00
commit d609aa3f79
81 changed files with 1715 additions and 1337 deletions

14
.env.dusk.testing Normal file
View file

@ -0,0 +1,14 @@
APP_ENV=testing
APP_DEBUG=true
APP_KEY=base64:6DJhvZLVjE6dD4Cqrteh+6Z5vZlG+v/soCKcDHLOAH0=
APP_URL=http://localhost:8000
APP_LONGURL=localhost
APP_SHORTURL=local
DB_CONNECTION=travis
CACHE_DRIVER=array
SESSION_DRIVER=file
QUEUE_DRIVER=sync
SCOUT_DRIVER=pgsql

View file

@ -1,5 +1,6 @@
APP_ENV=testing
APP_KEY=
APP_DEBUG=true
APP_KEY=base64:6DJhvZLVjE6dD4Cqrteh+6Z5vZlG+v/soCKcDHLOAH0=
APP_URL=http://localhost:8000
APP_LONGURL=localhost
APP_SHORTURL=local

1
.gitignore vendored
View file

@ -10,3 +10,4 @@ Homestead.json
/public/files
/public/keybase.txt
/coverage
/LegacyTests

View file

@ -3,8 +3,22 @@ language: php
sudo: false
dist: trusty
cache:
- apt
addons:
postgresql: "9.5"
postgresql: "9.6"
apt:
sources:
- sourceline: 'deb http://ppa.launchpad.net/nginx/development/ubuntu trusty main'
packages:
- nginx
- realpath
artifacts:
s3_region: "eu-west-1"
paths:
- $(ls tests/Browser/screenshots/*.png | tr "\n" ":")
- $(ls /tmp/*.log | tr "\n" ":")
services:
- postgresql
@ -22,6 +36,10 @@ matrix:
- php: nightly
before_install:
- mkdir travis-phantomjs
- wget https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2 -O $PWD/travis-phantomjs/phantomjs-2.1.1-linux-x86_64.tar.bz2
- tar -xvf $PWD/travis-phantomjs/phantomjs-2.1.1-linux-x86_64.tar.bz2 -C $PWD/travis-phantomjs
- export PATH=$PWD/travis-phantomjs/phantomjs-2.1.1-linux-x86_64/bin:$PATH
- phpenv config-rm xdebug.ini || echo "xdebug already absent"
- travis_retry composer self-update --preview
@ -29,6 +47,7 @@ install:
- if [[ $setup = 'basic' ]]; then travis_retry composer install --no-interaction --prefer-dist; fi
- if [[ $setup = 'stable' ]]; then travis_retry composer update --no-interaction --prefer-dist --prefer-stable; fi
- if [[ $setup = 'lowest' ]]; then travis_retry composer update --no-interaction --prefer-dist --prefer-lowest --prefer-stable; fi
- travis/install-nginx.sh
before_script:
- psql -U travis -c 'create database travis_ci_test'
@ -37,10 +56,10 @@ before_script:
- php artisan key:generate
- php artisan migrate
- php artisan db:seed
- php artisan token:generate
- php artisan serve &
- phantomjs --webdriver=127.0.0.1:9515 --webdriver-loglevel=DEBUG &
- sleep 5 # Give artisan some time to start serving
script:
- phpdbg -qrr vendor/bin/phpunit --coverage-text
- php artisan dusk --filter syndication
- php artisan security:check

View file

@ -3,7 +3,8 @@
.PHONY: sass frontend js compress lint-sass lint-js
jsfiles := $(wildcard resources/assets/js/*.js)
sassfiles := $(wildcard resources/assets/sass/*.scss)
yarnfiles:= node_modules/mapbox-gl/dist/mapbox-gl.css
yarnfiles:= node_modules/mapbox-gl/dist/mapbox-gl.css \
node_modules/alertify.js/dist/css/alertify.css
assets := public/assets/css/app.css \
public/assets/prism/prism.css public/assets/prism/prism.js \
$(wildcard public/assets/js/*.js) \

View file

@ -48,7 +48,7 @@ class GenerateToken extends Command
{
$data = [
'me' => config('app.url'),
'client_id' => config('app.url') . '/notes/new',
'client_id' => route('micropub-client'),
'scope' => 'post',
];
$token = $tokenService->getNewToken($data);

View file

@ -1,6 +1,8 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
class AdminController extends Controller
{

View file

@ -1,30 +1,19 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Article;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class ArticlesAdminController extends Controller
{
/**
* Show the new article form.
*
* @return \Illuminate\View\Factory view
*/
public function newArticle()
{
$message = session('message');
return view('admin.newarticle', ['message' => $message]);
}
/**
* List the articles that can be edited.
*
* @return \Illuminate\View\Factory view
*/
public function listArticles()
public function index()
{
$posts = Article::select('id', 'title', 'published')->orderBy('id', 'desc')->get();
@ -32,32 +21,15 @@ class ArticlesAdminController extends Controller
}
/**
* Show the edit form for an existing article.
* Show the new article form.
*
* @param string The article id
* @return \Illuminate\View\Factory view
*/
public function editArticle($articleId)
public function create()
{
$post = Article::select(
'title',
'main',
'url',
'published'
)->where('id', $articleId)->get();
$message = session('message');
return view('admin.editarticle', ['id' => $articleId, 'post' => $post]);
}
/**
* Show the delete confirmation form for an article.
*
* @param string The article id
* @return \Illuminate\View\Factory view
*/
public function deleteArticle($articleId)
{
return view('admin.deletearticle', ['id' => $articleId]);
return view('admin.newarticle', ['message' => $message]);
}
/**
@ -66,7 +38,7 @@ class ArticlesAdminController extends Controller
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\Factory view
*/
public function postNewArticle(Request $request)
public function store(Request $request)
{
$published = $request->input('published');
if ($published == null) {
@ -102,14 +74,32 @@ class ArticlesAdminController extends Controller
return view('admin.newarticlesuccess', ['id' => $article->id, 'title' => $article->title]);
}
/**
* Show the edit form for an existing article.
*
* @param string The article id
* @return \Illuminate\View\Factory view
*/
public function edit($articleId)
{
$post = Article::select(
'title',
'main',
'url',
'published'
)->where('id', $articleId)->get();
return view('admin.editarticle', ['id' => $articleId, 'post' => $post]);
}
/**
* Process an incoming request to edit an article.
*
* @param string
* @param \Illuminate\Http\Request $request
* @param string
* @return \Illuminate|View\Factory view
*/
public function postEditArticle($articleId, Request $request)
public function update(Request $request, $articleId)
{
$published = $request->input('published');
if ($published == null) {
@ -125,13 +115,24 @@ class ArticlesAdminController extends Controller
return view('admin.editarticlesuccess', ['id' => $articleId]);
}
/**
* Show the delete confirmation form for an article.
*
* @param string The article id
* @return \Illuminate\View\Factory view
*/
public function delete($articleId)
{
return view('admin.deletearticle', ['id' => $articleId]);
}
/**
* Process a request to delete an aricle.
*
* @param string The article id
* @return \Illuminate\View\Factory view
*/
public function postDeleteArticle($articleId)
public function destroy($articleId)
{
Article::where('id', $articleId)->delete();

View file

@ -1,8 +1,9 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\MicropubClient;
use App\Http\Controllers\Controller;
class ClientsAdminController extends Controller
{
@ -11,7 +12,7 @@ class ClientsAdminController extends Controller
*
* @return \Illuminate\View\Factory view
*/
public function listClients()
public function index()
{
$clients = MicropubClient::all();
@ -23,34 +24,18 @@ class ClientsAdminController extends Controller
*
* @return \Illuminate\View\Factory view
*/
public function newClient()
public function create()
{
return view('admin.newclient');
}
/**
* Process the request to adda new client name.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\Factory view
*/
public function postNewClient(Request $request)
{
MicropubClient::create([
'client_url' => $request->input('client_url'),
'client_name' => $request->input('client_name'),
]);
return view('admin.newclientsuccess');
}
/**
* Show a form to edit a client name.
*
* @param string The client id
* @return \Illuminate\View\Factory view
*/
public function editClient($clientId)
public function edit($clientId)
{
$client = MicropubClient::findOrFail($clientId);
@ -61,6 +46,22 @@ class ClientsAdminController extends Controller
]);
}
/**
* Process the request to adda new client name.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\Factory view
*/
public function store(Request $request)
{
MicropubClient::create([
'client_url' => $request->input('client_url'),
'client_name' => $request->input('client_name'),
]);
return view('admin.newclientsuccess');
}
/**
* Process the request to edit a client name.
*
@ -68,7 +69,7 @@ class ClientsAdminController extends Controller
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\Factory view
*/
public function postEditClient($clientId, Request $request)
public function update($clientId, Request $request)
{
$client = MicropubClient::findOrFail($clientId);
if ($request->input('edit')) {

View file

@ -1,43 +1,44 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Contact;
use GuzzleHttp\Client;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Filesystem\Filesystem;
class ContactsAdminController extends Controller
{
/**
* Display the form to add a new contact.
*
* @return \Illuminate\View\Factory view
*/
public function newContact()
{
return view('admin.newcontact');
}
/**
* List the currect contacts that can be edited.
*
* @return \Illuminate\View\Factory view
*/
public function listContacts()
public function index()
{
$contacts = Contact::all();
return view('admin.listcontacts', ['contacts' => $contacts]);
}
/**
* Display the form to add a new contact.
*
* @return \Illuminate\View\Factory view
*/
public function create()
{
return view('admin.newcontact');
}
/**
* Show the form to edit an existing contact.
*
* @param string The contact id
* @return \Illuminate\View\Factory view
*/
public function editContact($contactId)
public function edit($contactId)
{
$contact = Contact::findOrFail($contactId);
@ -49,7 +50,7 @@ class ContactsAdminController extends Controller
*
* @return \Illuminate\View\Factory view
*/
public function deleteContact($contactId)
public function delete($contactId)
{
return view('admin.deletecontact', ['id' => $contactId]);
}
@ -60,7 +61,7 @@ class ContactsAdminController extends Controller
* @param \Illuminate\Http|request $request
* @return \Illuminate\View\Factory view
*/
public function postNewContact(Request $request)
public function store(Request $request)
{
$contact = new Contact();
$contact->name = $request->input('name');
@ -82,7 +83,7 @@ class ContactsAdminController extends Controller
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\Factory view
*/
public function postEditContact($contactId, Request $request)
public function update($contactId, Request $request)
{
$contact = Contact::findOrFail($contactId);
$contact->name = $request->input('name');
@ -113,7 +114,7 @@ class ContactsAdminController extends Controller
* @param string The contact id
* @return \Illuminate\View\Factory view
*/
public function postDeleteContact($contactId)
public function destroy($contactId)
{
$contact = Contact::findOrFail($contactId);
$contact->delete();

View file

@ -1,12 +1,13 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Note;
use Validator;
use Illuminate\Http\Request;
use App\Jobs\SendWebMentions;
use App\Services\NoteService;
use App\Http\Controllers\Controller;
class NotesAdminController extends Controller
{
@ -17,22 +18,12 @@ class NotesAdminController extends Controller
$this->noteService = $noteService ?? new NoteService();
}
/**
* Show the form to make a new note.
*
* @return \Illuminate\View\Factory view
*/
public function newNotePage()
{
return view('admin.newnote');
}
/**
* List the notes that can be edited.
*
* @return \Illuminate\View\Factory view
*/
public function listNotesPage()
public function index()
{
$notes = Note::select('id', 'note')->orderBy('id', 'desc')->get();
foreach ($notes as $note) {
@ -43,14 +34,13 @@ class NotesAdminController extends Controller
}
/**
* The delete note page.
* Show the form to make a new note.
*
* @param int id
* @return view
* @return \Illuminate\View\Factory view
*/
public function deleteNotePage($id)
public function create()
{
return view('admin.deletenote', ['id' => $id]);
return view('admin.newnote');
}
/**
@ -59,7 +49,7 @@ class NotesAdminController extends Controller
* @param string The note id
* @return \Illuminate\View\Factory view
*/
public function editNotePage($noteId)
public function edit($noteId)
{
$note = Note::find($noteId);
$note->originalNote = $note->getOriginal('note');
@ -67,13 +57,24 @@ class NotesAdminController extends Controller
return view('admin.editnote', ['id' => $noteId, 'note' => $note]);
}
/**
* The delete note page.
*
* @param int id
* @return view
*/
public function delete($noteId)
{
return view('admin.deletenote', ['id' => $id]);
}
/**
* Process a request to make a new note.
*
* @param Illuminate\Http\Request $request
* @todo Sort this mess out
*/
public function createNote(Request $request)
public function store(Request $request)
{
$validator = Validator::make(
$request->all(),
@ -101,7 +102,7 @@ class NotesAdminController extends Controller
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\Factory view
*/
public function editNote($noteId, Request $request)
public function update($noteId, Request $request)
{
//update note data
$note = Note::findOrFail($noteId);
@ -122,7 +123,7 @@ class NotesAdminController extends Controller
* @param int id
* @return view
*/
public function deleteNote($id)
public function destroy($id)
{
$note = Note::findOrFail($id);
$note->delete();

View file

@ -1,9 +1,10 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Place;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Phaza\LaravelPostgis\Geometries\Point;
class PlacesAdminController extends Controller
@ -13,7 +14,7 @@ class PlacesAdminController extends Controller
*
* @return \Illuminate\View\Factory view
*/
public function listPlacesPage()
public function index()
{
$places = Place::all();
@ -25,7 +26,7 @@ class PlacesAdminController extends Controller
*
* @return \Illuminate\View\Factory view
*/
public function newPlacePage()
public function create()
{
return view('admin.newplace');
}
@ -36,7 +37,7 @@ class PlacesAdminController extends Controller
* @param string The place id
* @return \Illuminate\View\Factory view
*/
public function editPlacePage($placeId)
public function edit($placeId)
{
$place = Place::findOrFail($placeId);
@ -58,7 +59,7 @@ class PlacesAdminController extends Controller
* @param Illuminate\Http\Request $request
* @return Illuminate\View\Factory view
*/
public function createPlace(Request $request)
public function store(Request $request)
{
$this->placeService->createPlace($request);
@ -72,7 +73,7 @@ class PlacesAdminController extends Controller
* @param Illuminate\Http\Request $request
* @return Illuminate\View\Factory view
*/
public function editPlace($placeId, Request $request)
public function update($placeId, Request $request)
{
$place = Place::findOrFail($placeId);
$place->name = $request->name;

View file

@ -39,11 +39,11 @@ class ContactsController extends Controller
$contact = Contact::where('nick', '=', $nick)->firstOrFail();
$contact->homepageHost = parse_url($contact->homepage, PHP_URL_HOST);
$file = public_path() . '/assets/profile-images/' . $contact->homepageHost . '/image';
$contact->image = ($filesystem->exists($file)) ?
$image = ($filesystem->exists($file)) ?
'/assets/profile-images/' . $contact->homepageHost . '/image'
:
'/assets/profile-images/default-image';
return view('contacts.show', ['contact' => $contact]);
return view('contacts.show', compact('contact', 'image'));
}
}

View file

@ -62,7 +62,7 @@ class MicropubClientController extends Controller
$this->indieClient
);
if (! $micropubEndpoint) {
return redirect(route('micropub-client'))->withErrors('Unable to determine micropub API endpoint', 'endpoint');
return redirect(route('micropub-client'))->with('error', 'Unable to determine micropub API endpoint');
}
$response = $this->postNoteRequest($request, $micropubEndpoint, $token);
@ -76,7 +76,7 @@ class MicropubClientController extends Controller
return redirect($location);
}
return redirect(route('micropub-client'))->withErrors('Endpoint didnt create the note.', 'endpoint');
return redirect(route('micropub-client'))->with('error', 'Endpoint didnt create the note.');
}
/**
@ -87,8 +87,6 @@ class MicropubClientController extends Controller
* and syndicate-to
*
* @param \Illuminate\Http\Request $request
* @param \IndieAuth\Client $indieClient
* @param \GuzzleHttp\Client $guzzleClient
* @return \Illuminate\Routing\Redirector redirect
*/
public function refreshSyndicationTargets(Request $request)
@ -97,7 +95,7 @@ class MicropubClientController extends Controller
$token = $request->session()->get('token');
$micropubEndpoint = $this->indieAuthService->discoverMicropubEndpoint($domain, $this->indieClient);
if (! $micropubEndpoint) {
return redirect(route('micropub-client'))->withErrors('Unable to determine micropub API endpoint', 'endpoint');
return redirect(route('micropub-client'))->with('error', 'Unable to determine micropub API endpoint');
}
try {
@ -106,7 +104,7 @@ class MicropubClientController extends Controller
'query' => ['q' => 'syndicate-to'],
]);
} catch (\GuzzleHttp\Exception\BadResponseException $e) {
return redirect(route('micropub-client'))->withErrors('Bad response when refreshing syndication targets', 'endpoint');
return redirect(route('micropub-client'))->with('error', 'Bad response when refreshing syndication targets');
}
$body = (string) $response->getBody();
$syndication = $this->parseSyndicationTargets($body);
@ -180,8 +178,7 @@ class MicropubClientController extends Controller
'headers' => $headers,
]);
} catch (\GuzzleHttp\Exception\BadResponseException $e) {
return redirect(route('micropub-client'))
->withErrors('There was a bad response from the micropub endpoint.', 'endpoint');
return redirect(route('micropub-client'))->with('error', 'There was a bad response from the micropub endpoint.');
}
return $response;
@ -335,6 +332,8 @@ class MicropubClientController extends Controller
'name' => $syn['name'],
];
}
} else {
$syndicateTo[] = ['target' => 'http://example.org', 'name' => 'Joe Bloggs on Example'];
}
if (count($syndicateTo) > 0) {
return $syndicateTo;

View file

@ -3,7 +3,6 @@
namespace App\Http\Controllers;
use App\Place;
use Illuminate\Http\Request;
class PlacesController extends Controller
{
@ -19,27 +18,6 @@ class PlacesController extends Controller
return view('allplaces', ['places' => $places]);
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*
@ -48,42 +26,8 @@ class PlacesController extends Controller
*/
public function show($slug)
{
$place = Place::where('slug', '=', $slug)->first();
$place = Place::where('slug', '=', $slug)->firstOrFail();
return view('singleplace', ['place' => $place]);
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
}
}

View file

@ -20,6 +20,14 @@ class DevTokenMiddleware
session(['me' => config('app.url')]);
if (Storage::exists('dev-token')) {
session(['token' => Storage::get('dev-token')]);
} else {
$data = [
'me' => config('app.url'),
'client_id' => route('micropub-client'),
'scope' => 'post',
];
$tokenService = new \App\Services\TokenService();
session(['token' => $tokenService->getNewToken($data)]);
}
}

View file

@ -5,6 +5,7 @@ namespace App\Providers;
use App\Tag;
use App\Note;
use Validator;
use Laravel\Dusk\DuskServiceProvider;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
@ -54,6 +55,8 @@ class AppServiceProvider extends ServiceProvider
*/
public function register()
{
//
if ($this->app->environment('local', 'testing')) {
$this->app->register(DuskServiceProvider::class);
}
}
}

View file

@ -29,14 +29,12 @@
"laravel/tinker": "^1.0"
},
"require-dev": {
"fzaninotto/faker": "~1.4",
"mockery/mockery": "0.9.*",
"phpunit/phpunit": "~5.7",
"symfony/css-selector": "3.1.*",
"symfony/dom-crawler": "3.1.*",
"barryvdh/laravel-debugbar": "~2.0",
"fzaninotto/faker": "~1.4",
"jakub-onderka/php-parallel-lint": "^0.9.2",
"laravel/browser-kit-testing": "^1.0"
"laravel/dusk": "^1.0",
"mockery/mockery": "0.9.*",
"phpunit/phpunit": "~5.7"
},
"autoload": {
"classmap": [
@ -47,10 +45,9 @@
}
},
"autoload-dev": {
"classmap": [
"tests/TestCase.php",
"tests/BrowserKitTest.php"
]
"psr-4": {
"Tests\\": "tests"
}
},
"scripts": {
"post-root-package-install": [

281
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"content-hash": "16af842252275cac279c7f118ad6e6d4",
"content-hash": "e4236aef74a9e56de4a85dc50bf32dcf",
"packages": [
{
"name": "anahkiasen/underscore-php",
@ -58,16 +58,16 @@
},
{
"name": "aws/aws-sdk-php",
"version": "3.22.4",
"version": "3.22.7",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "916f708c1a643f86f74eacd3c5be787b40d814f8"
"reference": "eea83aaac2b6c86f72a5c85c54d1839b70d4fd21"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/916f708c1a643f86f74eacd3c5be787b40d814f8",
"reference": "916f708c1a643f86f74eacd3c5be787b40d814f8",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/eea83aaac2b6c86f72a5c85c54d1839b70d4fd21",
"reference": "eea83aaac2b6c86f72a5c85c54d1839b70d4fd21",
"shasum": ""
},
"require": {
@ -134,7 +134,7 @@
"s3",
"sdk"
],
"time": "2017-02-14T21:23:54+00:00"
"time": "2017-02-17T20:09:40+00:00"
},
{
"name": "barnabywalters/mf-cleaner",
@ -2965,16 +2965,16 @@
},
{
"name": "sensiolabs/security-checker",
"version": "v4.0.0",
"version": "v4.0.1",
"source": {
"type": "git",
"url": "https://github.com/sensiolabs/security-checker.git",
"reference": "116027b57b568ed61b7b1c80eeb4f6ee9e8c599c"
"reference": "f2ce0035fc512287978510ca1740cd111d60f89f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sensiolabs/security-checker/zipball/116027b57b568ed61b7b1c80eeb4f6ee9e8c599c",
"reference": "116027b57b568ed61b7b1c80eeb4f6ee9e8c599c",
"url": "https://api.github.com/repos/sensiolabs/security-checker/zipball/f2ce0035fc512287978510ca1740cd111d60f89f",
"reference": "f2ce0035fc512287978510ca1740cd111d60f89f",
"shasum": ""
},
"require": {
@ -3005,7 +3005,7 @@
}
],
"description": "A security checker for your composer.lock",
"time": "2016-09-23T18:09:57+00:00"
"time": "2017-02-18T17:53:25+00:00"
},
{
"name": "spatie/laravel-glide",
@ -3280,16 +3280,16 @@
},
{
"name": "symfony/console",
"version": "v3.2.3",
"version": "v3.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "7a8405a9fc175f87fed8a3c40856b0d866d61936"
"reference": "0e5e6899f82230fcb1153bcaf0e106ffaa44b870"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/7a8405a9fc175f87fed8a3c40856b0d866d61936",
"reference": "7a8405a9fc175f87fed8a3c40856b0d866d61936",
"url": "https://api.github.com/repos/symfony/console/zipball/0e5e6899f82230fcb1153bcaf0e106ffaa44b870",
"reference": "0e5e6899f82230fcb1153bcaf0e106ffaa44b870",
"shasum": ""
},
"require": {
@ -3339,20 +3339,20 @@
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
"time": "2017-02-06T12:04:21+00:00"
"time": "2017-02-16T14:07:22+00:00"
},
{
"name": "symfony/css-selector",
"version": "v3.1.10",
"version": "v3.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
"reference": "722a87478a72d95dc2a3bcf41dc9c2d13fd4cb2d"
"reference": "f0e628f04fc055c934b3211cfabdb1c59eefbfaa"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/css-selector/zipball/722a87478a72d95dc2a3bcf41dc9c2d13fd4cb2d",
"reference": "722a87478a72d95dc2a3bcf41dc9c2d13fd4cb2d",
"url": "https://api.github.com/repos/symfony/css-selector/zipball/f0e628f04fc055c934b3211cfabdb1c59eefbfaa",
"reference": "f0e628f04fc055c934b3211cfabdb1c59eefbfaa",
"shasum": ""
},
"require": {
@ -3361,7 +3361,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.1-dev"
"dev-master": "3.2-dev"
}
},
"autoload": {
@ -3392,20 +3392,20 @@
],
"description": "Symfony CssSelector Component",
"homepage": "https://symfony.com",
"time": "2017-01-02T20:31:54+00:00"
"time": "2017-01-02T20:32:22+00:00"
},
{
"name": "symfony/debug",
"version": "v3.2.3",
"version": "v3.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/debug.git",
"reference": "b4d9818f127c60ce21ed62c395da7df868dc8477"
"reference": "9b98854cb45bc59d100b7d4cc4cf9e05f21026b9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/debug/zipball/b4d9818f127c60ce21ed62c395da7df868dc8477",
"reference": "b4d9818f127c60ce21ed62c395da7df868dc8477",
"url": "https://api.github.com/repos/symfony/debug/zipball/9b98854cb45bc59d100b7d4cc4cf9e05f21026b9",
"reference": "9b98854cb45bc59d100b7d4cc4cf9e05f21026b9",
"shasum": ""
},
"require": {
@ -3449,11 +3449,11 @@
],
"description": "Symfony Debug Component",
"homepage": "https://symfony.com",
"time": "2017-01-28T02:37:08+00:00"
"time": "2017-02-16T16:34:18+00:00"
},
{
"name": "symfony/event-dispatcher",
"version": "v3.2.3",
"version": "v3.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
@ -3513,7 +3513,7 @@
},
{
"name": "symfony/finder",
"version": "v3.2.3",
"version": "v3.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
@ -3562,16 +3562,16 @@
},
{
"name": "symfony/http-foundation",
"version": "v3.2.3",
"version": "v3.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
"reference": "e192b04de44aa1ed0e39d6793f7e06f5e0b672a0"
"reference": "a90da6dd679605d88c9803a57a6fc1fb7a19a6e0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/e192b04de44aa1ed0e39d6793f7e06f5e0b672a0",
"reference": "e192b04de44aa1ed0e39d6793f7e06f5e0b672a0",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/a90da6dd679605d88c9803a57a6fc1fb7a19a6e0",
"reference": "a90da6dd679605d88c9803a57a6fc1fb7a19a6e0",
"shasum": ""
},
"require": {
@ -3611,20 +3611,20 @@
],
"description": "Symfony HttpFoundation Component",
"homepage": "https://symfony.com",
"time": "2017-02-02T13:47:35+00:00"
"time": "2017-02-16T22:46:52+00:00"
},
{
"name": "symfony/http-kernel",
"version": "v3.2.3",
"version": "v3.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
"reference": "96443239baf674b143604fb87cb27cb01672ab77"
"reference": "4cd0d4bc31819095c6ef13573069f621eb321081"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/96443239baf674b143604fb87cb27cb01672ab77",
"reference": "96443239baf674b143604fb87cb27cb01672ab77",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/4cd0d4bc31819095c6ef13573069f621eb321081",
"reference": "4cd0d4bc31819095c6ef13573069f621eb321081",
"shasum": ""
},
"require": {
@ -3693,7 +3693,7 @@
],
"description": "Symfony HttpKernel Component",
"homepage": "https://symfony.com",
"time": "2017-02-06T13:15:19+00:00"
"time": "2017-02-16T23:59:56+00:00"
},
{
"name": "symfony/polyfill-mbstring",
@ -3756,16 +3756,16 @@
},
{
"name": "symfony/process",
"version": "v3.2.3",
"version": "v3.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "32646a7cf53f3956c76dcb5c82555224ae321858"
"reference": "0ab87c1e7570b3534a6e51eb4ca8e9f6d7327856"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/32646a7cf53f3956c76dcb5c82555224ae321858",
"reference": "32646a7cf53f3956c76dcb5c82555224ae321858",
"url": "https://api.github.com/repos/symfony/process/zipball/0ab87c1e7570b3534a6e51eb4ca8e9f6d7327856",
"reference": "0ab87c1e7570b3534a6e51eb4ca8e9f6d7327856",
"shasum": ""
},
"require": {
@ -3801,11 +3801,11 @@
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com",
"time": "2017-02-03T12:11:38+00:00"
"time": "2017-02-16T14:07:22+00:00"
},
{
"name": "symfony/routing",
"version": "v3.2.3",
"version": "v3.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/routing.git",
@ -3880,16 +3880,16 @@
},
{
"name": "symfony/translation",
"version": "v3.2.3",
"version": "v3.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
"reference": "ca032cc56976d88b85e7386b17020bc6dc95dbc5"
"reference": "d6825c6bb2f1da13f564678f9f236fe8242c0029"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/translation/zipball/ca032cc56976d88b85e7386b17020bc6dc95dbc5",
"reference": "ca032cc56976d88b85e7386b17020bc6dc95dbc5",
"url": "https://api.github.com/repos/symfony/translation/zipball/d6825c6bb2f1da13f564678f9f236fe8242c0029",
"reference": "d6825c6bb2f1da13f564678f9f236fe8242c0029",
"shasum": ""
},
"require": {
@ -3940,20 +3940,20 @@
],
"description": "Symfony Translation Component",
"homepage": "https://symfony.com",
"time": "2017-01-21T17:06:35+00:00"
"time": "2017-02-16T22:46:52+00:00"
},
{
"name": "symfony/var-dumper",
"version": "v3.2.3",
"version": "v3.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
"reference": "5bb4435a03a4f05c211f4a9a8ee2756965924511"
"reference": "cb50260b674ee1c2d4ab49f2395a42e0b4681e20"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/5bb4435a03a4f05c211f4a9a8ee2756965924511",
"reference": "5bb4435a03a4f05c211f4a9a8ee2756965924511",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/cb50260b674ee1c2d4ab49f2395a42e0b4681e20",
"reference": "cb50260b674ee1c2d4ab49f2395a42e0b4681e20",
"shasum": ""
},
"require": {
@ -4003,7 +4003,7 @@
"debug",
"dump"
],
"time": "2017-01-24T12:58:58+00:00"
"time": "2017-02-16T22:46:52+00:00"
},
{
"name": "themattharris/tmhoauth",
@ -4298,6 +4298,55 @@
],
"time": "2015-06-14T21:17:01+00:00"
},
{
"name": "facebook/webdriver",
"version": "1.3.0",
"source": {
"type": "git",
"url": "https://github.com/facebook/php-webdriver.git",
"reference": "77300c4ab2025d4316635f592ec849ca7323bd8c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/facebook/php-webdriver/zipball/77300c4ab2025d4316635f592ec849ca7323bd8c",
"reference": "77300c4ab2025d4316635f592ec849ca7323bd8c",
"shasum": ""
},
"require": {
"ext-curl": "*",
"php": "^5.5 || ~7.0",
"symfony/process": "^2.8 || ^3.1"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^1.11",
"php-mock/php-mock-phpunit": "^1.1",
"phpunit/phpunit": "4.6.* || ~5.0",
"satooshi/php-coveralls": "^1.0",
"squizlabs/php_codesniffer": "^2.6"
},
"suggest": {
"phpdocumentor/phpdocumentor": "2.*"
},
"type": "library",
"autoload": {
"psr-4": {
"Facebook\\WebDriver\\": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"description": "A PHP client for WebDriver",
"homepage": "https://github.com/facebook/php-webdriver",
"keywords": [
"facebook",
"php",
"selenium",
"webdriver"
],
"time": "2017-01-13T15:48:08+00:00"
},
{
"name": "fzaninotto/faker",
"version": "v1.6.0",
@ -4439,23 +4488,30 @@
"time": "2015-12-15T10:42:16+00:00"
},
{
"name": "laravel/browser-kit-testing",
"version": "v1.0.3",
"name": "laravel/dusk",
"version": "v1.0.6",
"source": {
"type": "git",
"url": "https://github.com/laravel/browser-kit-testing.git",
"reference": "0adfb725147815bff5516d157577f375a6e66ebd"
"url": "https://github.com/laravel/dusk.git",
"reference": "804bf2ef20de7d86caac1aff433c761399b55e56"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/browser-kit-testing/zipball/0adfb725147815bff5516d157577f375a6e66ebd",
"reference": "0adfb725147815bff5516d157577f375a6e66ebd",
"url": "https://api.github.com/repos/laravel/dusk/zipball/804bf2ef20de7d86caac1aff433c761399b55e56",
"reference": "804bf2ef20de7d86caac1aff433c761399b55e56",
"shasum": ""
},
"require": {
"php": ">=5.5.9",
"symfony/css-selector": "~3.1",
"symfony/dom-crawler": "~3.1"
"facebook/webdriver": "~1.0",
"illuminate/support": "~5.3",
"nesbot/carbon": "~1.20",
"php": ">=5.6.4",
"symfony/console": "~3.2",
"symfony/process": "~3.2"
},
"require-dev": {
"mockery/mockery": "^0.9.6",
"phpunit/phpunit": "^5.7"
},
"type": "library",
"extra": {
@ -4465,7 +4521,7 @@
},
"autoload": {
"psr-4": {
"Laravel\\BrowserKitTesting\\": "src/"
"Laravel\\Dusk\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
@ -4478,12 +4534,13 @@
"email": "taylor@laravel.com"
}
],
"description": "Provides backwards compatibility for BrowserKit testing in Laravel 5.4.",
"description": "Laravel Dusk provides simple end-to-end testing and browser automation.",
"keywords": [
"laravel",
"testing"
"testing",
"webdriver"
],
"time": "2017-02-08T22:32:37+00:00"
"time": "2017-02-15T20:21:39+00:00"
},
{
"name": "maximebf/debugbar",
@ -5108,16 +5165,16 @@
},
{
"name": "phpunit/phpunit",
"version": "5.7.13",
"version": "5.7.14",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "60ebeed87a35ea46fd7f7d8029df2d6f013ebb34"
"reference": "4906b8faf23e42612182fd212eb6f4c0f2954b57"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/60ebeed87a35ea46fd7f7d8029df2d6f013ebb34",
"reference": "60ebeed87a35ea46fd7f7d8029df2d6f013ebb34",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4906b8faf23e42612182fd212eb6f4c0f2954b57",
"reference": "4906b8faf23e42612182fd212eb6f4c0f2954b57",
"shasum": ""
},
"require": {
@ -5141,7 +5198,7 @@
"sebastian/global-state": "^1.1",
"sebastian/object-enumerator": "~2.0",
"sebastian/resource-operations": "~1.0",
"sebastian/version": "~1.0|~2.0",
"sebastian/version": "~1.0.3|~2.0",
"symfony/yaml": "~2.1|~3.0"
},
"conflict": {
@ -5186,7 +5243,7 @@
"testing",
"xunit"
],
"time": "2017-02-10T09:05:10+00:00"
"time": "2017-02-19T07:22:16+00:00"
},
{
"name": "phpunit/phpunit-mock-objects",
@ -5578,16 +5635,16 @@
},
{
"name": "sebastian/object-enumerator",
"version": "2.0.0",
"version": "2.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/object-enumerator.git",
"reference": "96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35"
"reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35",
"reference": "96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35",
"url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1311872ac850040a79c3c058bea3e22d0f09cbb7",
"reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7",
"shasum": ""
},
"require": {
@ -5620,7 +5677,7 @@
],
"description": "Traverses array structures and object graphs to enumerate all referenced objects",
"homepage": "https://github.com/sebastianbergmann/object-enumerator/",
"time": "2016-11-19T07:35:10+00:00"
"time": "2017-02-18T15:18:39+00:00"
},
{
"name": "sebastian/recursion-context",
@ -5760,74 +5817,18 @@
"homepage": "https://github.com/sebastianbergmann/version",
"time": "2016-10-03T07:35:21+00:00"
},
{
"name": "symfony/dom-crawler",
"version": "v3.1.10",
"source": {
"type": "git",
"url": "https://github.com/symfony/dom-crawler.git",
"reference": "7eede2a901a19928494194f7d1815a77b9a473a0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/7eede2a901a19928494194f7d1815a77b9a473a0",
"reference": "7eede2a901a19928494194f7d1815a77b9a473a0",
"shasum": ""
},
"require": {
"php": ">=5.5.9",
"symfony/polyfill-mbstring": "~1.0"
},
"require-dev": {
"symfony/css-selector": "~2.8|~3.0"
},
"suggest": {
"symfony/css-selector": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.1-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\DomCrawler\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony DomCrawler Component",
"homepage": "https://symfony.com",
"time": "2017-01-21T17:13:55+00:00"
},
{
"name": "symfony/yaml",
"version": "v3.2.3",
"version": "v3.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "e1718c6bf57e1efbb8793ada951584b2ab27775b"
"reference": "9724c684646fcb5387d579b4bfaa63ee0b0c64c8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/e1718c6bf57e1efbb8793ada951584b2ab27775b",
"reference": "e1718c6bf57e1efbb8793ada951584b2ab27775b",
"url": "https://api.github.com/repos/symfony/yaml/zipball/9724c684646fcb5387d579b4bfaa63ee0b0c64c8",
"reference": "9724c684646fcb5387d579b4bfaa63ee0b0c64c8",
"shasum": ""
},
"require": {
@ -5869,7 +5870,7 @@
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
"time": "2017-01-21T17:06:35+00:00"
"time": "2017-02-16T22:46:52+00:00"
},
{
"name": "webmozart/assert",

View file

@ -10,7 +10,7 @@ return [
| The username of the admin account.
*/
'name' => env('ADMIN_NAME'),
'user' => env('ADMIN_USER'),
/*
|--------------------------------------------------------------------------

View file

@ -161,7 +161,7 @@ return [
|
*/
'secure' => true,
'secure' => (config('app.env') != 'testing'),
/*
|--------------------------------------------------------------------------

View file

@ -9,16 +9,16 @@
processIsolation="false"
stopOnFailure="false">
<testsuites>
<testsuite name="Application Test Suite">
<directory suffix="Test.php">./tests</directory>
<testsuite name="Feature Tests">
<directory suffix="Test.php">./tests/Feature</directory>
</testsuite>
<testsuite name="Unit Tests">
<directory suffix="Test.php">./tests/Unit</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./app</directory>
<exclude>
<file>./app/Http/routes.php</file>
</exclude>
</whitelist>
</filter>
<php>

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

View file

@ -1 +1,16 @@
{"version":3,"sources":["../../../resources/assets/sass/app.scss","../../../resources/assets/sass/layout.scss","../../../resources/assets/sass/styles.scss","../../../resources/assets/sass/pagination.scss","../../../resources/assets/sass/note-form.scss","../../../resources/assets/sass/mapbox.scss","../../../resources/assets/sass/contacts.scss","../../../resources/assets/sass/emoji.scss"],"names":[],"mappings":"AAIA,KACI,sBACA,cAAe,CAClB,qBAKG,kBAAmB,CACtB,KCVG,eACA,cACA,iBACA,kBACA,oBAAqB,CACxB,WAGG,iBAAkB,CACrB,SAGG,gBAAiB,CACpB,MAGG,oBACA,AADA,aACA,4BAAsB,AAAtB,6BAAsB,AAAtB,qBAAsB,CACzB,eAGG,oBACA,AADA,aACA,8BACA,AADA,6BACA,AADA,mBACA,yBACA,AADA,8BACA,gBAAiB,CACpB,cAGG,oBACA,AADA,aACA,yBAAmB,AAAnB,kBAAmB,CACtB,kBAGG,gBAAiB,CACpB,iBAGG,qBACA,WAAY,CACf,aAGG,eACA,yBAA0B,CAC7B,OAGG,eAAgB,CACnB,cAGG,eAAgB,CACnB,WAGG,eACA,cACA,iBAAkB,CACrB,sBAGG,cAAe,CAClB,sBAGG,iBACA,cAAe,CAClB,WAGG,kBACA,WACA,SACA,qBAAsB,CACzB,SAGG,kBACA,MACA,OACA,WACA,WAAY,CACf,KCjFG,6JAWc,CACjB,EAGG,qBACA,wBACA,UAAW,CACd,gBAGG,kBAAmB,CACtB,MAGG,WACA,UAAW,CACd,OAGG,iBACA,iBAAkB,CACrB,WAGG,kBAAmB,CACtB,UAGG,YACA,WAAY,CACf,YC1CG,WACA,YACA,oBACA,AADA,aACA,8BACA,AADA,6BACA,AADA,mBACA,yBACA,AADA,8BACA,yBAAmB,AAAnB,kBAAmB,CACtB,eAGG,oBAAqB,CACxB,SCVG,oBACA,AADA,aACA,4BAAsB,AAAtB,6BAAsB,AAAtB,qBAAsB,CACzB,0BAGG,aACI,oBACA,AADA,aACA,8BACA,AADA,6BACA,AADA,mBACA,cAAe,CAClB,CAGL,0BACI,sBACI,UAAW,CACd,CAGL,eACI,UACA,oBACA,gBAAiB,CACpB,oDAIG,mBAAO,AAAP,MAAO,CACV,kBAGG,qBAAsB,CACzB,QAGG,mBAAoB,CACvB,KCnCG,eACA,YAAa,CAChB,QAGG,y4HACA,wBACA,WACA,WAAY,CACf,UAGG,kBACA,MACA,OACA,iBACA,cAAe,CAClB,gBAGG,gBACA,gBAAiB,CACpB,SCtBG,oBACA,AADA,aACA,8BACA,AADA,6BACA,AADA,mBACA,eACA,6BAA8B,CACjC,aAGG,oBACA,YACA,YAAa,CAChB,sDCPG,iBAAkB,CACrB,gFAIG,kBACA,cACA,UACA,aACA,OACA,cACA,qBACA,yBACA,oBACA,oCACA,yBACA,kCACA,WACA,cACA,0CAAkC,AAAlC,iCAAkC,CACrC,2BAGG,KACI,aACA,6BACA,wCACA,0BACA,qBAAkC,CAGtC,GACI,aACA,kCACA,yBACA,WACA,mCAAgD,CAAA,CAIxD,AApBC,mBAGG,KACI,aACA,6BACA,wCACA,0BACA,qBAAkC,CAGtC,GACI,aACA,kCACA,yBACA,WACA,mCAAgD,CAAA,CAIxD,aACI,kCACI,kCAAmC,CACtC,CAAA","file":"app.css"}
{
"version": 3,
"file": "app.css",
"sources": [
"../../../resources/assets/sass/app.scss",
"../../../resources/assets/sass/layout.scss",
"../../../resources/assets/sass/styles.scss",
"../../../resources/assets/sass/pagination.scss",
"../../../resources/assets/sass/note-form.scss",
"../../../resources/assets/sass/mapbox.scss",
"../../../resources/assets/sass/contacts.scss",
"../../../resources/assets/sass/emoji.scss"
],
"names": [],
"mappings": "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"
}

1
public/assets/frontend/alertify.css vendored Normal file
View file

@ -0,0 +1 @@
.alertify-logs>*{padding:12px 24px;color:#fff;box-shadow:0 2px 5px 0 rgba(0,0,0,.2);border-radius:1px}.alertify-logs>*,.alertify-logs>.default{background:rgba(0,0,0,.8)}.alertify-logs>.error{background:rgba(244,67,54,.8)}.alertify-logs>.success{background:rgba(76,175,80,.9)}.alertify{position:fixed;background-color:rgba(0,0,0,.3);left:0;right:0;top:0;bottom:0;width:100%;height:100%;z-index:1}.alertify.hide{opacity:0;pointer-events:none}.alertify,.alertify.show{box-sizing:border-box;transition:all .33s cubic-bezier(.25,.8,.25,1)}.alertify,.alertify *{box-sizing:border-box}.alertify .dialog{padding:12px}.alertify .alert,.alertify .dialog{width:100%;margin:0 auto;position:relative;top:50%;transform:translateY(-50%)}.alertify .alert>*,.alertify .dialog>*{width:400px;max-width:95%;margin:0 auto;text-align:center;padding:12px;background:#fff;box-shadow:0 2px 4px -1px rgba(0,0,0,.14),0 4px 5px 0 rgba(0,0,0,.098),0 1px 10px 0 rgba(0,0,0,.084)}.alertify .alert .msg,.alertify .dialog .msg{padding:12px;margin-bottom:12px;margin:0;text-align:left}.alertify .alert input:not(.form-control),.alertify .dialog input:not(.form-control){margin-bottom:15px;width:100%;font-size:100%;padding:12px}.alertify .alert input:not(.form-control):focus,.alertify .dialog input:not(.form-control):focus{outline-offset:-2px}.alertify .alert nav,.alertify .dialog nav{text-align:right}.alertify .alert nav button:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button),.alertify .dialog nav button:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button){background:transparent;box-sizing:border-box;color:rgba(0,0,0,.87);position:relative;outline:0;border:0;display:inline-block;-ms-flex-align:center;-ms-grid-row-align:center;align-items:center;padding:0 6px;margin:6px 8px;line-height:36px;min-height:36px;white-space:nowrap;min-width:88px;text-align:center;text-transform:uppercase;font-size:14px;text-decoration:none;cursor:pointer;border:1px solid transparent;border-radius:2px}.alertify .alert nav button:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button):active,.alertify .alert nav button:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button):hover,.alertify .dialog nav button:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button):active,.alertify .dialog nav button:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button):hover{background-color:rgba(0,0,0,.05)}.alertify .alert nav button:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button):focus,.alertify .dialog nav button:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button):focus{border:1px solid rgba(0,0,0,.1)}.alertify .alert nav button.btn,.alertify .dialog nav button.btn{margin:6px 4px}.alertify-logs{position:fixed;z-index:1}.alertify-logs.bottom,.alertify-logs:not(.top){bottom:16px}.alertify-logs.left,.alertify-logs:not(.right){left:16px}.alertify-logs.left>*,.alertify-logs:not(.right)>*{float:left;transform:translateZ(0);height:auto}.alertify-logs.left>.show,.alertify-logs:not(.right)>.show{left:0}.alertify-logs.left>*,.alertify-logs.left>.hide,.alertify-logs:not(.right)>*,.alertify-logs:not(.right)>.hide{left:-110%}.alertify-logs.right{right:16px}.alertify-logs.right>*{float:right;transform:translateZ(0)}.alertify-logs.right>.show{right:0;opacity:1}.alertify-logs.right>*,.alertify-logs.right>.hide{right:-110%;opacity:0}.alertify-logs.top{top:0}.alertify-logs>*{box-sizing:border-box;transition:all .4s cubic-bezier(.25,.8,.25,1);position:relative;clear:both;backface-visibility:hidden;perspective:1000;max-height:0;margin:0;padding:0;overflow:hidden;opacity:0;pointer-events:none}.alertify-logs>.show{margin-top:12px;opacity:1;max-height:1000px;padding:12px;pointer-events:auto}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -6,6 +6,6 @@ Contacts «
@section('content')
@foreach($contacts as $contact)
@include('templates.contact', ['contact' => $contact])
@include('templates.contact', ['contact' => $contact, 'image' => $contact->image])
@endforeach
@stop

View file

@ -1,9 +1,9 @@
@extends('master')
@section('title')
Contacts «
Contacts «
@stop
@section('content')
@include('templates.contact', array('contact' => $contact))
@include('templates.contact', ['contact' => $contact, 'image' => $image])
@stop

View file

@ -5,13 +5,10 @@ New Note «
@stop
@section('content')
@if (session('error'))
<p class="error">{{ session('error') }}</p>
@endif
<p>This is my UI for posting new notes, hopefully youll soon be able to use this if your site supports the micropub API.</p>
@if($errors->endpoint->first() != '')
<p class="error">{{ $errors->endpoint->first() }}</p>
@endif
@if($errors->indieauth->first() != '')
<p class="error">{{ $errors->indieauth->first() }}</p>
@endif
@if($url === null)
<form action="{{ route('indieauth-start') }}" method="post">
<input type="hidden" name="_token" value="{{ csrf_token() }}">

View file

@ -1,6 +1,6 @@
<div class="h-card contact">
<div>
<img src="{{ $contact->image }}" alt="" class="u-photo">
<img src="{{ $image }}" alt="" class="u-photo">
</div>
<div>
<span class="p-name">{{ $contact->name }}</span> <a href="/contacts/{{ $contact->nick }}">{{ '@' . $contact->nick }}</a>

View file

@ -36,7 +36,7 @@
</div>
@endif
<div>
<a href="/refresh-syndication-targets">Refresh Syndication Targets</a>
<a href="/micropub/refresh-syndication-targets">Refresh Syndication Targets</a>
</div>
@endif
<div>

View file

@ -31,26 +31,26 @@ Route::group(['domain' => config('url.longurl')], function () {
Route::post('login', 'AuthController@login');
//Admin pages grouped for filter
Route::group(['middleware' => 'myauth'], function () {
Route::group(['middleware' => 'myauth', 'namespace' => 'Admin'], function () {
Route::get('admin', 'AdminController@showWelcome');
//Articles
Route::get('admin/blog/new', 'ArticlesAdminController@newArticle');
Route::get('admin/blog/edit', 'ArticlesAdminController@listArticles');
Route::get('admin/blog/edit/{id}', 'ArticlesAdminController@editArticle');
Route::get('admin/blog/delete/{id}', 'ArticlesAdminController@deleteArticle');
Route::post('admin/blog/new', 'ArticlesAdminController@postNewArticle');
Route::post('admin/blog/edit/{id}', 'ArticlesAdminController@postEditArticle');
Route::post('admin/blog/delete/{id}', 'ArticlesAdminController@postDeleteArticle');
Route::get('admin/blog/new', 'ArticlesAdminController@create');
Route::get('admin/blog/edit', 'ArticlesAdminController@index');
Route::get('admin/blog/edit/{id}', 'ArticlesAdminController@edit');
Route::get('admin/blog/delete/{id}', 'ArticlesAdminController@delete');
Route::post('admin/blog/new', 'ArticlesAdminController@store');
Route::post('admin/blog/edit/{id}', 'ArticlesAdminController@update');
Route::post('admin/blog/delete/{id}', 'ArticlesAdminController@detroy');
//Notes
Route::get('admin/note/new', 'NotesAdminController@newNotePage');
Route::get('admin/note/edit', 'NotesAdminController@listNotesPage');
Route::get('admin/note/edit/{id}', 'NotesAdminController@editNotePage');
Route::get('admin/note/delete/{id}', 'NotesAdminController@deleteNotePage');
Route::post('admin/note/new', 'NotesAdminController@createNote');
Route::post('admin/note/edit/{id}', 'NotesAdminController@editNote');
Route::post('admin/note/delete/{id}', 'NotesAdminController@deleteNote');
Route::get('admin/note/edit', 'NotesAdminController@index');
Route::get('admin/note/new', 'NotesAdminController@create');
Route::get('admin/note/edit/{id}', 'NotesAdminController@edit');
Route::get('admin/note/delete/{id}', 'NotesAdminController@delete');
Route::post('admin/note/new', 'NotesAdminController@store');
Route::post('admin/note/edit/{id}', 'NotesAdminController@update');
Route::post('admin/note/delete/{id}', 'NotesAdminController@destroy');
//Tokens
Route::get('admin/tokens', 'TokensController@showTokens');
@ -58,28 +58,28 @@ Route::group(['domain' => config('url.longurl')], function () {
Route::post('admin/tokens/delete/{id}', 'TokensController@postDeleteToken');
//Micropub Clients
Route::get('admin/clients', 'ClientsAdminController@listClients');
Route::get('admin/clients/new', 'ClientsAdminController@newClient');
Route::get('admin/clients/edit/{id}', 'ClientsAdminController@editClient');
Route::post('admin/clients/new', 'ClientsAdminController@postNewClient');
Route::post('admin/clients/edit/{id}', 'ClientsAdminController@postEditClient');
Route::get('admin/clients', 'ClientsAdminController@index');
Route::get('admin/clients/new', 'ClientsAdminController@create');
Route::get('admin/clients/edit/{id}', 'ClientsAdminController@edit');
Route::post('admin/clients/new', 'ClientsAdminController@store');
Route::post('admin/clients/edit/{id}', 'ClientsAdminController@update');
//Contacts
Route::get('admin/contacts/new', 'ContactsAdminController@newContact');
Route::get('admin/contacts/edit', 'ContactsAdminController@listContacts');
Route::get('admin/contacts/edit/{id}', 'ContactsAdminController@editContact');
Route::get('admin/contacts/edit', 'ContactsAdminController@index');
Route::get('admin/contacts/new', 'ContactsAdminController@create');
Route::get('admin/contacts/edit/{id}', 'ContactsAdminController@edit');
Route::get('admin/contacts/delete/{id}', 'ContactsAdminController@delete');
Route::post('admin/contacts/new', 'ContactsAdminController@store');
Route::post('admin/contacts/edit/{id}', 'ContactsAdminController@update');
Route::post('admin/contacts/delete/{id}', 'ContactsAdminController@destroy');
Route::get('admin/contacts/edit/{id}/getavatar', 'ContactsAdminController@getAvatar');
Route::get('admin/contacts/delete/{id}', 'ContactsAdminController@deleteContact');
Route::post('admin/contacts/new', 'ContactsAdminController@postNewContact');
Route::post('admin/contacts/edit/{id}', 'ContactsAdminController@postEditContact');
Route::post('admin/contacts/delete/{id}', 'ContactsAdminController@postDeleteContact');
//Places
Route::get('admin/places/new', 'PlacesAdminController@newPlacePage');
Route::get('admin/places/edit', 'PlacesAdminController@listPlacesPage');
Route::get('admin/places/edit/{id}', 'PlacesAdminController@editPlacePage');
Route::post('admin/places/new', 'PlacesAdminController@createPlace');
Route::post('admin/places/edit/{id}', 'PlacesAdminController@editPlace');
Route::get('admin/places/edit', 'PlacesAdminController@index');
Route::get('admin/places/new', 'PlacesAdminController@create');
Route::get('admin/places/edit/{id}', 'PlacesAdminController@edit');
Route::post('admin/places/new', 'PlacesAdminController@store');
Route::post('admin/places/edit/{id}', 'PlacesAdminController@update');
});
//Blog pages using ArticlesController

View file

@ -1,78 +0,0 @@
<?php
namespace App\Tests;
use BrowserKitTest;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class ArticlesTest extends BrowserKitTest
{
protected $appurl;
public function setUp()
{
parent::setUp();
$this->appurl = config('app.url');
}
/**
* Test the `/blog` page returns the article, this
* means the database is being hit.
*
* @return void
*/
public function testArticlesPage()
{
$this->visit($this->appurl . '/blog')
->see('My New Blog');
}
/**
* Test the `/blog/{year}` page returns the article, this
* means the database is being hit.
*
* @return void
*/
public function testArticlesYearPage()
{
$this->visit($this->appurl . '/blog/2016')
->see('My New Blog');
}
/**
* Test the `/blog/{year}/{month}` page returns the article,
* this means the database is being hit.
*
* @return void
*/
public function testArticlesMonthPage()
{
$this->visit($this->appurl . '/blog/2016/01')
->see('My New Blog');
}
/**
* Test a single article page.
*
* @return void
*/
public function testSingleArticlePage()
{
$this->visit($this->appurl . '/blog/2016/01/my-new-blog')
->see('My New Blog');
}
/**
* Test the RSS feed.
*
* @return void
*/
public function testRSSFeed()
{
$response = $this->call('GET', $this->appurl . '/feed');
$this->assertEquals('application/rss+xml', $response->headers->get('Content-Type'));
}
}

View file

@ -0,0 +1,61 @@
<?php
namespace Tests\Browser;
use Tests\DuskTestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;
class ArticlesTest extends DuskTestCase
{
/**
* Test the `/blog` page.
*
* @return void
*/
public function test_articles_page()
{
$this->browse(function ($browser) {
$browser->visit('/blog')
->assertSee('My New Blog');
});
}
/**
* Test the `/blog` page with a year scoping results.
*
* @return void
*/
public function test_articles_page_with_specified_year()
{
$this->browse(function ($browser) {
$browser->visit('/blog/2016')
->assertSee('My New Blog');
});
}
/**
* Test the `/blog` page with a year and month scoping results.
*
* @return void
*/
public function test_articles_page_with_specified_year_and_month()
{
$this->browse(function ($browser) {
$browser->visit('/blog/2016/01')
->assertSee('My New Blog');
});
}
/**
* Test a single article page.
*
* @return void
*/
public function test_single_article_page()
{
$this->browse(function ($browser) {
$browser->visit('/blog/2016/01/my-new-blog')
->assertSee('My New Blog');
});
}
}

View file

@ -0,0 +1,23 @@
<?php
namespace Tests\Browser;
use Tests\DuskTestCase;
use Laravel\Dusk\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
class ExampleTest extends DuskTestCase
{
/**
* A basic browser test example.
*
* @return void
*/
public function testBasicExample()
{
$this->browse(function (Browser $browser) {
$browser->visit('/')
->assertSee('Built with love');
});
}
}

View file

@ -0,0 +1,47 @@
<?php
namespace Tests\Browser;
use Tests\DuskTestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;
class MicropubClientTest extends DuskTestCase
{
/**
* Test the client is shown for an unauthorised request.
*
* @return void
*/
public function test_client_page_see_authenticated()
{
$this->browse(function ($browser) {
$browser->visit(route('micropub-client'))
->assertSee('You are authenticated');
});
}
public function test_client_page_creates_new_note()
{
$faker = \Faker\Factory::create();
$note = 'Fake note from #LaravelDusk: ' . $faker->text;
$this->browse(function ($browser) use ($note) {
$browser->visit(route('micropub-client'))
->type('content', $note)
->press('Submit');
});
$this->assertDatabaseHas('notes', ['note' => $note]);
$newNote = \App\Note::where('note', $note)->first();
$newNote->forceDelete();
}
public function test_client_page_updates_syndication()
{
$this->browse(function ($browser) {
$browser->visit(route('micropub-client'))
->assertDontSee('jonnybarnes on Twitter')
->clickLink('Refresh Syndication Targets')
->pause(5000)
->assertSee('jonnybarnes on Twitter');
});
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace Tests\Browser;
use Tests\DuskTestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;
class NotesTest extends DuskTestCase
{
/**
* Look for the client name after the note.
*
* @return void
*/
public function test_client_name_displayed()
{
$this->browse(function ($browser) {
$browser->visit('/notes/D')
->assertSee('JBL5');
});
}
/**
* Look for the client URL after the note.
*
* @return void
*/
public function test_client_url_displayed()
{
$this->browse(function ($browser) {
$browser->visit('/notes/E')
->assertSee('quill.p3k.io');
});
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace Tests\Browser\Pages;
use Laravel\Dusk\Browser;
class HomePage extends Page
{
/**
* Get the URL for the page.
*
* @return string
*/
public function url()
{
return '/';
}
/**
* Assert that the browser is on the page.
*
* @return void
*/
public function assert(Browser $browser)
{
//
}
/**
* Get the element shortcuts for the page.
*
* @return array
*/
public function elements()
{
return [
'@element' => '#selector',
];
}
}

View file

@ -0,0 +1,20 @@
<?php
namespace Tests\Browser\Pages;
use Laravel\Dusk\Page as BasePage;
abstract class Page extends BasePage
{
/**
* Get the global element shortcuts for the site.
*
* @return array
*/
public static function siteElements()
{
return [
'@element' => '#selector',
];
}
}

2
tests/Browser/screenshots/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*
!.gitignore

View file

@ -1,52 +0,0 @@
<?php
namespace App\Tests;
use BrowserKitTest;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class ContactsTest extends BrowserKitTest
{
protected $appurl;
public function setUp()
{
parent::setUp();
$this->appurl = config('app.url');
}
/**
* Test the `/contacts` page and see if response is OK.
*
* @return void
*/
public function testContactsPage()
{
$this->visit($this->appurl . '/contacts')
->assertResponseOK();
}
/**
* Test an individual contact page with default profile image.
*
* @return void
*/
public function testContactPageWithDefaultPic()
{
$this->visit($this->appurl . '/contacts/tantek')
->see('<img src="/assets/profile-images/default-image" alt="" class="u-photo">');
}
/**
* Test an individual contact page with a specific profile image.
*
* @return void
*/
public function testContactPageWithSpecificPic()
{
$this->visit($this->appurl . '/contacts/aaron')
->see('<img src="/assets/profile-images/aaronparecki.com/image" alt="" class="u-photo">');
}
}

View file

@ -1,17 +1,11 @@
<?php
namespace Tests;
use Illuminate\Contracts\Console\Kernel;
use Laravel\BrowserKitTesting\TestCase as BaseTestCase;
abstract class BrowserKitTest extends BaseTestCase
trait CreatesApplication
{
/**
* The base URL to use while testing the application.
*
* @var string
*/
protected $baseUrl = 'http://localhost';
/**
* Creates the application.
*
@ -20,9 +14,7 @@ abstract class BrowserKitTest extends BaseTestCase
public function createApplication()
{
$app = require __DIR__.'/../bootstrap/app.php';
$app->make(Kernel::class)->bootstrap();
return $app;
}
}

35
tests/DuskTestCase.php Normal file
View file

@ -0,0 +1,35 @@
<?php
namespace Tests;
use Laravel\Dusk\TestCase as BaseTestCase;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
abstract class DuskTestCase extends BaseTestCase
{
use CreatesApplication;
/**
* Prepare for Dusk test execution.
*
* @beforeClass
* @return void
*/
public static function prepare()
{
//static::startChromeDriver();
}
/**
* Create the RemoteWebDriver instance.
*
* @return \Facebook\WebDriver\Remote\RemoteWebDriver
*/
protected function driver()
{
return RemoteWebDriver::create(
'http://localhost:9515', DesiredCapabilities::phantomjs()
);
}
}

View file

@ -0,0 +1,22 @@
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class ArticlesRSSTest extends TestCase
{
/**
* Test the RSS feed.
*
* @return void
*/
public function test_rss_feed()
{
$response = $this->get('/feed');
$response->assertHeader('Content-Type', 'application/rss+xml');
}
}

View file

@ -0,0 +1,44 @@
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class ContactsTest extends TestCase
{
/**
* Check the `/contacts` page gives a good response.
*
* @return void
*/
public function test_contacts_page()
{
$response = $this->get('/contacts');
$response->assertStatus(200);
}
/**
* Test an individual contact page with default profile image.
*
* @return void
*/
public function test_contact_page_with_default_pic()
{
$response = $this->get('/contacts/tantek');
$response->assertViewHas('image', '/assets/profile-images/default-image');
}
/**
* Test an individual contact page with a specific profile image.
*
* @return void
*/
public function test_contact_page_with_specific_pic()
{
$response = $this->get('/contacts/aaron');
$response->assertViewHas('image', '/assets/profile-images/aaronparecki.com/image');
}
}

View file

@ -0,0 +1,22 @@
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* @return void
*/
public function testBasicTest()
{
$response = $this->get('/');
$response->assertStatus(200);
}
}

View file

@ -0,0 +1,51 @@
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class IndieAuthControllerTest extends TestCase
{
/**
* Test the `start` method redirects to the client on error.
*
* @return void
*/
public function test_indieauthcontroller_begin_auth_flow_redirects_back_to_client_on_error()
{
$response = $this->call('GET', '/indieauth/start', ['me' => 'http://example.org']);
$this->assertSame(config('app.url') . '/micropub/create', $response->headers->get('Location'));
}
/**
* Now we test the `start` method as a whole.
*
* @return void
*/
public function test_indieauthcontroller_begin_auth_redirects_to_endpoint()
{
$response = $this->call('GET', '/indieauth/start', ['me' => config('app.url')]);
$this->assertSame(
'https://indieauth.com/auth?me=',
substr($response->headers->get('Location'), 0, 30)
);
}
/**
* Test the `callback` method.
*
* @return void
*/
public function test_indieauthcontroller_callback_method_gives_error_with_mismatched_state()
{
$response = $this->withSession(['state' => 'state-session'])
->call(
'GET',
'indieauth/callback',
['me', config('app.url'), 'state' => 'request-session']
);
$response->assertSessionHasErrors();
}
}

View file

@ -0,0 +1,316 @@
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class MicropubControllerTest extends TestCase
{
use DatabaseTransactions;
/**
* Test a GET request for the micropub endpoint without a token gives a
* 400 response. Also check the error message.
*
* @return void
*/
public function test_micropub_request_without_token_returns_400_response()
{
$response = $this->get('/api/post');
$response->assertStatus(400);
$response->assertJsonFragment(['error_description' => 'No token provided with request']);
}
/**
* Test a GET request for the micropub endpoint without a valid token gives
* a 400 response. Also check the error message.
*
* @return void
*/
public function test_micropub_request_without_valid_token_returns_400_response()
{
$response = $this->call('GET', '/api/post', [], [], [], ['HTTP_Authorization' => 'Bearer abc123']);
$response->assertStatus(400);
$response->assertJsonFragment(['error_description' => 'The provided token did not pass validation']);
}
/**
* Test a GET request for the micropub endpoint with a valid token gives a
* 200 response. Check token information is returned in the response.
*
* @return void
*/
public function test_micropub_request_with_valid_token_returns_200_response()
{
$response = $this->call('GET', '/api/post', [], [], [], ['HTTP_Authorization' => 'Bearer ' . $this->getToken()]);
$response->assertStatus(200);
$response->assertJsonFragment(['response' => 'token']);
}
/**
* Test a GET request for syndication targets.
*
* @return void
*/
public function test_micropub_request_for_syndication_targets()
{
$response = $this->call('GET', '/api/post', ['q' => 'syndicate-to'], [], [], ['HTTP_Authorization' => 'Bearer ' . $this->getToken()]);
$response->assertJsonFragment(['uid' => 'https://twitter.com/jonnybarnes']);
}
/**
* Test a request for places.
*
* @return void
*/
public function test_micropub_request_for_nearby_places()
{
$response = $this->call('GET', '/api/post', ['q' => 'geo:53.5,-2.38'], [], [], ['HTTP_Authorization' => 'Bearer ' . $this->getToken()]);
$response->assertJson(['places' => [['slug' =>'the-bridgewater-pub']]]);
}
/**
* Test a request for places, this time with an uncertainty parameter.
*
* @return void
*/
public function test_micropub_request_for_nearby_places_with_uncertainty_parameter()
{
$response = $this->call('GET', '/api/post', ['q' => 'geo:53.5,-2.38;u=35'], [], [], ['HTTP_Authorization' => 'Bearer ' . $this->getToken()]);
$response->assertJson(['places' => [['slug' => 'the-bridgewater-pub']]]);
}
/**
* Test a request for places, where there will be an “empty” response.
*
* @return void
*/
public function test_micropub_request_for_nearby_places_where_non_exist()
{
$response = $this->call('GET', '/api/post', ['q' => 'geo:1.23,4.56'], [], [], ['HTTP_Authorization' => 'Bearer ' . $this->getToken()]);
$response->assertJson(['places' => []]);
}
/**
* Test a request for the micropub config.
*
* @return void
*/
public function test_micropub_request_for_config()
{
$response = $this->call('GET', '/api/post', ['q' => 'config'], [], [], ['HTTP_Authorization' => 'Bearer ' . $this->getToken()]);
$response->assertJsonFragment(['uid' => 'https://twitter.com/jonnybarnes']);
}
/**
* Test a valid micropub requests creates a new note.
*
* @return void
*/
public function test_micropub_request_creates_new_note()
{
$faker = \Faker\Factory::create();
$note = $faker->text;
$response = $this->call(
'POST',
'/api/post',
[
'h' => 'entry',
'content' => $note
],
[],
[],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$response->assertJson(['response' => 'created']);
$this->assertDatabaseHas('notes', ['note' => $note]);
}
/**
* Test a valid micropub requests creates a new place.
*
* @return void
*/
public function test_micropub_request_creates_new_place()
{
$response = $this->call(
'POST',
'/api/post',
[
'h' => 'card',
'name' => 'The Barton Arms',
'geo' => 'geo:53.4974,-2.3768'
],
[],
[],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$response->assertJson(['response' => 'created']);
$this->assertDatabaseHas('places', ['slug' => 'the-barton-arms']);
}
/**
* Test a valid micropub requests using JSON syntax creates a new note.
*
* @return void
*/
public function test_micropub_request_with_json_syntax_creates_new_note()
{
$faker = \Faker\Factory::create();
$note = $faker->text;
$response = $this->json(
'POST',
'/api/post',
[
'type' => ['h-entry'],
'properties' => [
'content' => [$note],
],
],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$response
->assertStatus(201)
->assertJson(['response' => 'created']);
}
/**
* Test a micropub requests using JSON syntax without a token returns an
* error. Also check the message.
*
* @return void
*/
public function test_micropub_request_with_json_syntax_without_token_returns_error()
{
$faker = \Faker\Factory::create();
$note = $faker->text;
$response = $this->json(
'POST',
'/api/post',
[
'type' => ['h-entry'],
'properties' => [
'content' => [$note],
],
]
);
$response
->assertJson([
'response' => 'error',
'error' => 'no_token'
])
->assertStatus(400);
}
/**
* Test a micropub requests using JSON syntax without a valis token returns
* an error. Also check the message.
*
* @return void
*/
public function test_micropub_request_with_json_syntax_with_invalid_token_returns_error()
{
$faker = \Faker\Factory::create();
$note = $faker->text;
$response = $this->json(
'POST',
'/api/post',
[
'type' => ['h-entry'],
'properties' => [
'content' => [$note],
],
],
['HTTP_Authorization' => 'Bearer ' . $this->getInvalidToken()]
);
$response
->assertJson([
'response' => 'error',
'error' => 'invalid_token'
])
->assertStatus(400);
}
public function test_micropub_request_with_json_syntax_creates_new_place()
{
$faker = \Faker\Factory::create();
$response = $this->json(
'POST',
'/api/post',
[
'type' => ['h-card'],
'properties' => [
'name' => $faker->name,
'geo' => 'geo:' . $faker->latitude . ',' . $faker->longitude
],
],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$response
->assertJson(['response' => 'created'])
->assertStatus(201);
}
public function test_micropub_request_with_json_syntax_and_uncertainty_parameter_creates_new_place()
{
$faker = \Faker\Factory::create();
$response = $this->json(
'POST',
'/api/post',
[
'type' => ['h-card'],
'properties' => [
'name' => $faker->name,
'geo' => 'geo:' . $faker->latitude . ',' . $faker->longitude . ';u=35'
],
],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$response
->assertJson(['response' => 'created'])
->assertStatus(201);
}
/**
* Generate a valid token to be used in the tests.
*
* @return Lcobucci\JWT\Token\Plain $token
*/
private function getToken()
{
$signer = new Sha256();
$token = (new Builder())
->set('client_id', 'https://quill.p3k.io')
->set('me', 'https://jonnybarnes.localhost')
->set('scope', 'post')
->set('issued_at', time())
->sign($signer, env('APP_KEY'))
->getToken();
return $token;
}
/**
* Generate an invalid token to be used in the tests.
*
* @return Lcobucci\JWT\Token\Plain $token
*/
private function getInvalidToken()
{
$signer = new Sha256();
$token = (new Builder())
->set('client_id', 'https://quill.p3k.io')
->set('me', 'https://jonnybarnes.localhost')
->set('scope', 'view') //error here
->set('issued_at', time())
->sign($signer, env('APP_KEY'))
->getToken();
return $token;
}
}

View file

@ -0,0 +1,57 @@
<?php
namespace Tests\Feature;
use App\Note;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class NotesControllerTest extends TestCase
{
/**
* Test the `/notes` page returns 200, this should
* mean the database is being hit.
*
* @return void
*/
public function test_notes_page()
{
$response = $this->get('/notes');
$response->assertStatus(200);
}
/**
* Test a specific note.
*
* @return void
*/
public function test_specific_note()
{
$response = $this->get('/notes/D');
$response->assertViewHas('note');
}
/**
* Test that `/note/{decID}` redirects to `/notes/{nb60id}`.
*
* @return void
*/
public function test_dec_id_redirect()
{
$response = $this->get('/note/11');
$response->assertRedirect(config('app.url') . '/notes/B');
}
/**
* Visit the tagged page and check the tag view data.
*
* @return void
*/
public function test_tagged_notes_page()
{
$response = $this->get('/notes/tagged/beer');
$response->assertViewHas('tag', 'beer');
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class PlacesTest extends TestCase
{
/**
* Test the `/places` page for OK response.
*
* @return void
*/
public function test_places_page()
{
$response = $this->get('/places');
$response->assertStatus(200);
}
/**
* Test a specific place.
*
* @return void
*/
public function test_single_place()
{
$place = \App\Place::where('slug', 'the-bridgewater-pub')->first();
$response = $this->get('/places/the-bridgewater-pub');
$response->assertViewHas('place', $place);
}
}

View file

@ -1,38 +1,30 @@
<?php
namespace App\Tests;
namespace Tests\Feature;
use BrowserKitTest;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class TokenServiceTest extends BrowserKitTest
class TokenServiceTest extends TestCase
{
protected $appurl;
public function setUp()
{
parent::setUp();
$this->appurl = config('app.url');
$this->tokenService = new \App\Services\TokenService();
}
/**
* Given the token is dependent on a random nonce, the time of creation and
* the APP_KEY, to test, we shall create a token, and then verify it.
*
* @return void
*/
public function testTokenCreationAndValidation()
public function test_token_creation_and_validation()
{
$tokenService = new \App\Services\TokenService();
$data = [
'me' => 'https://example.org',
'client_id' => 'https://quill.p3k.io',
'scope' => 'post'
];
$token = $this->tokenService->getNewToken($data);
$valid = $this->tokenService->validateToken($token);
$token = $tokenService->getNewToken($data);
$valid = $tokenService->validateToken($token);
$validData = [
'me' => $valid->getClaim('me'),
'client_id' => $valid->getClaim('client_id'),

View file

@ -0,0 +1,84 @@
<?php
namespace Tests\Feature;
use Tests\TestCase;
use App\Jobs\ProcessWebMention;
use Illuminate\Support\Facades\Queue;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class WebMentionsControllerTest extends TestCase
{
/**
* Test webmentions without source and target are rejected.
*
* @return void
*/
public function test_webmentions_without_source_and_target_are_rejected()
{
$response = $this->call('POST', '/webmention', ['source' => 'https://example.org/post/123']);
$response->assertStatus(400);
}
/**
* Test invalid target gets a 400 response.
*
* @return void
*/
public function test_invalid_target_returns_400_response()
{
$response = $this->call('POST', '/webmention', [
'source' => 'https://example.org/post/123',
'target' => config('app.url') . '/invalid/target'
]);
$response->assertStatus(400);
}
/**
* Test blog target gets a 501 response due to me not supporting it.
*
* @return void
*/
public function test_blog_target_returns_501_response()
{
$response = $this->call('POST', '/webmention', [
'source' => 'https://example.org/post/123',
'target' => config('app.url') . '/blog/target'
]);
$response->assertStatus(501);
}
/**
* Test that a non-existant note gives a 400 response.
*
* @return void
*/
public function test_nonexistant_note_returns_400_response()
{
$response = $this->call('POST', '/webmention', [
'source' => 'https://example.org/post/123',
'target' => config('app.url') . '/notes/ZZZZZ'
]);
$response->assertStatus(400);
}
/**
* Test a legit webmention triggers the ProcessWebMention job.
*
* @return void
*/
public function test_legitimate_webmnetion_triggers_processwebmention_job()
{
Queue::fake();
$response = $this->call('POST', '/webmention', [
'source' => 'https://example.org/post/123',
'target' => config('app.url') . '/notes/B'
]);
$response->assertStatus(202);
Queue::assertPushed(ProcessWebMention::class);
}
}

View file

@ -1,79 +0,0 @@
<?php
namespace App\Tests;
use BrowserKitTest;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class IndieAuthTest extends BrowserKitTest
{
protected $appurl;
public function setUp()
{
parent::setUp();
$this->appurl = config('app.url');
}
/**
* Test the getAuthorizationEndpoint calls the correct service methods,
* though these methods are actually mocked.
*
* @return void
*/
public function testIndieAuthServiceDiscoversEndpoint()
{
$service = new \App\Services\IndieAuthService();
$client = new \IndieAuth\Client();
$result = $service->getAuthorizationEndpoint($this->appurl, $client);
$this->assertSame('https://indieauth.com/auth', $result);
}
/**
* Test that the Service build the correct redirect URL.
*
* @return void
*/
public function testIndieAuthServiceBuildRedirectURL()
{
$client = new \IndieAuth\Client();
$service = new \App\Services\IndieAuthService();
$result = $service->buildAuthorizationURL(
'https://indieauth.com/auth',
$this->appurl,
$client
);
$this->assertSame(
'https://indieauth.com/auth?me=',
substr($result, 0, 30)
);
}
/**
* Test the `start` method redirects to the client on error.
*
* @return void
*/
public function testIndieAuthControllerBeginAuthRedirectsToClientOnFail()
{
$response = $this->call('GET', $this->appurl . '/indieauth/start', ['me' => 'http://example.org']);
$this->assertSame($this->appurl . '/micropub/create', $response->headers->get('Location'));
}
/**
* Now we test the `start` method as a whole.
*
* @return void
*/
public function testIndieAuthControllerBeginAuthRedirectsToEndpoint()
{
$response = $this->call('GET', $this->appurl . '/indieauth/start', ['me' => $this->appurl]);
$this->assertSame(
'https://indieauth.com/auth?me=',
substr($response->headers->get('Location'), 0, 30)
);
$response = null;
}
}

View file

@ -1,69 +0,0 @@
<?php
namespace App\Tests;
use BrowserKitTest;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class MicropubClientTest extends BrowserKitTest
{
protected $appurl;
public function setUp()
{
parent::setUp();
$this->appurl = config('app.url');
}
/**
* Test the client gets shown for an unauthorised request.
*
* @return void
*/
public function testClientPageUnauthorised()
{
$this->visit($this->appurl . '/micropub/create')
->see('IndieAuth');
}
public function testClientPageRecentAuth()
{
$this->visit($this->appurl . '/micropub/create')
->see($this->appurl);
}
public function testClientCreatesNewNoteWithTag()
{
//in this test, the syndication targets are blank
$faker = \Faker\Factory::create();
$note = 'Fake note from #PHPUnit: ' . $faker->text;
$this->visit($this->appurl . '/micropub/create')
->type($note, 'content')
->press('Submit');
$this->seeInDatabase('notes', ['note' => $note]);
$this->visit($this->appurl . '/notes/tagged/PHPUnit')
->see('PHPUnit');
//my client has made a request to my endpoint, which then adds
//to the db, so database transaction dont work
//so lets manually delete the new entry
//first, if we are using algolia, we need to delete it
if (env('SCOUT_DRIVER') == 'algolia') {
//we need to allow the index to update in order to query it
sleep(2);
$client = new \AlgoliaSearch\Client(env('ALGOLIA_APP_ID'), env('ALGOLIA_SECRET'));
$index = $client->initIndex('notes');
//here we query for the new note and tell algolia too delete it
$res = $index->deleteByQuery('Fake note from');
if ($res == 0) {
//somehow the new not didnt get deleted
$this->fail('Didnt delete the note from the index');
}
}
$newNote = \App\Note::where('note', $note)->first();
$newNote->forceDelete();
}
}

View file

@ -1,271 +0,0 @@
<?php
namespace App\Tests;
use BrowserKitTest;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class MicropubTest extends BrowserKitTest
{
use DatabaseTransactions;
protected $appurl;
public function setUp()
{
parent::setUp();
$this->appurl = config('app.url');
}
public function testMicropubRequestWithoutToken()
{
$this->call('GET', $this->appurl . '/api/post');
$this->assertResponseStatus(400);
$this->seeJson(['error_description' => 'No token provided with request']);
}
public function testMicropubRequestWithoutValidToken()
{
$this->call('GET', $this->appurl . '/api/post', [], [], [], ['HTTP_Authorization' => 'Bearer abc123']);
$this->assertResponseStatus(400);
$this->seeJson(['error_description' => 'The provided token did not pass validation']);
}
public function testMicropubRequestWithValidToken()
{
$this->call('GET', $this->appurl . '/api/post', [], [], [], ['HTTP_Authorization' => 'Bearer ' . $this->getToken()]);
$this->seeJson(['response' => 'token']);
}
public function testMicropubRequestForSyndication()
{
$this->call('GET', $this->appurl . '/api/post', ['q' => 'syndicate-to'], [], [], ['HTTP_Authorization' => 'Bearer ' . $this->getToken()]);
$this->seeJson(['uid' => 'https://twitter.com/jonnybarnes']);
}
public function testMicropubRequestForNearbyPlacesThatExist()
{
$this->call('GET', $this->appurl . '/api/post', ['q' => 'geo:53.5,-2.38'], [], [], ['HTTP_Authorization' => 'Bearer ' . $this->getToken()]);
$this->see('the-bridgewater-pub');
}
public function testMicropubRequestForNearbyPlacesThatExistWithUncertaintyParameter()
{
$this->call('GET', $this->appurl . '/api/post', ['q' => 'geo:53.5,-2.38;u=35'], [], [], ['HTTP_Authorization' => 'Bearer ' . $this->getToken()]);
$this->see('the-bridgewater-pub');
}
public function testMicropubRequestForNearbyPlacesThatDoNotExist()
{
$this->call('GET', $this->appurl . '/api/post', ['q' => 'geo:1.23,4.56'], [], [], ['HTTP_Authorization' => 'Bearer ' . $this->getToken()]);
$this->see('[]');
}
public function testMicropubRequestForConfig()
{
$this->call('GET', $this->appurl . '/api/post', ['q' => 'config'], [], [], ['HTTP_Authorization' => 'Bearer ' . $this->getToken()]);
$this->seeJson(['uid' => 'https://twitter.com/jonnybarnes']);
}
public function testMicropubRequestCreateNewNote()
{
$faker = \Faker\Factory::create();
$note = $faker->text;
$this->call(
'POST',
$this->appurl . '/api/post',
[
'h' => 'entry',
'content' => $note
],
[],
[],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$this->seeInDatabase('notes', ['note' => $note]);
}
public function testMicropubRequestCreateNewPlace()
{
$this->call(
'POST',
$this->appurl . '/api/post',
[
'h' => 'card',
'name' => 'The Barton Arms',
'geo' => 'geo:53.4974,-2.3768'
],
[],
[],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$this->seeInDatabase('places', ['slug' => 'the-barton-arms']);
}
public function testMicropubJSONRequestCreateNewNote()
{
$faker = \Faker\Factory::create();
$note = $faker->text;
$this->json(
'POST',
$this->appurl . '/api/post',
[
'type' => ['h-entry'],
'properties' => [
'content' => [$note],
],
],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
)->seeJson([
'response' => 'created'
])->assertResponseStatus(201);
}
public function testMicropubJSONRequestCreateNewNoteWithoutToken()
{
$faker = \Faker\Factory::create();
$note = $faker->text;
$this->json(
'POST',
$this->appurl . '/api/post',
[
'type' => ['h-entry'],
'properties' => [
'content' => [$note],
],
]
)->seeJson([
'response' => 'error',
'error' => 'no_token'
])->assertResponseStatus(400);
}
public function testMicropubJSONRequestCreateNewNoteWithInvalidToken()
{
$faker = \Faker\Factory::create();
$note = $faker->text;
$this->json(
'POST',
$this->appurl . '/api/post',
[
'type' => ['h-entry'],
'properties' => [
'content' => [$note],
],
],
['HTTP_Authorization' => 'Bearer ' . $this->getInvalidToken()]
)->seeJson([
'response' => 'error',
'error' => 'invalid_token'
]);
}
public function testMicropubJSONRequestCreateNewPlace()
{
$faker = \Faker\Factory::create();
$this->json(
'POST',
$this->appurl . '/api/post',
[
'type' => ['h-card'],
'properties' => [
'name' => $faker->name,
'geo' => 'geo:' . $faker->latitude . ',' . $faker->longitude
],
],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
)->seeJson([
'response' => 'created'
])->assertResponseStatus(201);
}
public function testMicropubJSONRequestCreateNewPlaceWithoutToken()
{
$faker = \Faker\Factory::create();
$this->json(
'POST',
$this->appurl . '/api/post',
[
'type' => ['h-entry'],
'properties' => [
'name' => $faker->name,
'geo' => 'geo:' . $faker->latitude . ',' . $faker->longitude
],
]
)->seeJson([
'response' => 'error',
'error' => 'no_token'
])->assertResponseStatus(400);
}
public function testMicropubJSONRequestCreateNewPlaceWithInvalidToken()
{
$faker = \Faker\Factory::create();
$this->json(
'POST',
$this->appurl . '/api/post',
[
'type' => ['h-entry'],
'properties' => [
'name' => $faker->name,
'geo' => 'geo:' . $faker->latitude . ',' . $faker->longitude
],
],
['HTTP_Authorization' => 'Bearer ' . $this->getInvalidToken()]
)->seeJson([
'response' => 'error',
'error' => 'invalid_token'
]);
}
public function testMicropubJSONRequestCreateNewPlaceWithUncertaintyParam()
{
$faker = \Faker\Factory::create();
$this->json(
'POST',
$this->appurl . '/api/post',
[
'type' => ['h-card'],
'properties' => [
'name' => $faker->name,
'geo' => 'geo:' . $faker->latitude . ',' . $faker->longitude . ';u=35'
],
],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
)->seeJson([
'response' => 'created'
])->assertResponseStatus(201);
}
private function getToken()
{
$signer = new Sha256();
$token = (new Builder())
->set('client_id', 'https://quill.p3k.io')
->set('me', 'https://jonnybarnes.localhost')
->set('scope', 'post')
->set('issued_at', time())
->sign($signer, env('APP_KEY'))
->getToken();
return $token;
}
private function getInvalidToken()
{
$signer = new Sha256();
$token = (new Builder())
->set('client_id', 'https://quill.p3k.io')
->set('me', 'https://jonnybarnes.localhost')
->set('scope', 'view')
->set('issued_at', time())
->sign($signer, env('APP_KEY'))
->getToken();
return $token;
}
}

View file

@ -1,33 +0,0 @@
<?php
namespace App\Tests;
use BrowserKitTest;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class NotesAdminTest extends BrowserKitTest
{
use DatabaseTransactions;
protected $appurl;
protected $notesAdminController;
public function setUp()
{
parent::setUp();
$this->appurl = config('app.url');
$this->notesAdminController = new \App\Http\Controllers\NotesAdminController();
}
public function testCreatedNoteDispatchesSendWebmentionsJob()
{
$this->expectsJobs(\App\Jobs\SendWebMentions::class);
$this->withSession(['loggedin' => true])
->visit($this->appurl . '/admin/note/new')
->type('Mentioning', 'content')
->press('Submit');
}
}

View file

@ -1,182 +0,0 @@
<?php
namespace App\Tests;
use Cache;
use BrowserKitTest;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class NotesTest extends BrowserKitTest
{
protected $appurl;
protected $notesController;
public function setUp()
{
parent::setUp();
$this->appurl = config('app.url');
$this->notesController = new \App\Http\Controllers\NotesController();
}
/**
* Test the `/notes` page returns 200, this should
* mean the database is being hit.
*
* @return void
*/
public function testNotesPage()
{
$this->visit($this->appurl . '/notes')
->assertResponseOk();
}
/**
* Test a specific note so that `singleNote()` get called.
*
* @return void
*/
public function testSpecificNote()
{
$this->visit($this->appurl . '/notes/B')
->see('#beer');
}
/**
* Test that `/note/{decID}` redirects to `/notes/{nb60id}`.
*
* @return void
*/
public function testDecIDRedirect()
{
$this->get($this->appurl . '/note/11')
->assertRedirectedTo(config('app.url') . '/notes/B');
}
/**
* Visit the tagged page and see text from the note.
*
* @return void
*/
public function testTaggedNotesPage()
{
$this->visit($this->appurl . '/notes/tagged/beer')
->see('at the local.');
}
/**
* Look for a default image in the contacts h-card.
*
* @return void
*/
public function testDefaultImageUsed()
{
$this->visit($this->appurl . '/notes/C')
->see('<img class="u-photo" alt="" src="/assets/profile-images/default-image">');
}
/**
* Look for a specific profile image in the contacts h-card.
*
* @return void
*/
public function testProfileImageUsed()
{
$this->visit($this->appurl . '/notes/D')
->see('<img class="u-photo" alt="" src="/assets/profile-images/aaronparecki.com/image">');
}
/**
* Look for twitter URL when theres no associated contact.
*
* @return void
*/
public function testTwitterLinkCreatedWhenNoContactFound()
{
$this->visit($this->appurl . '/notes/E')
->see('<a href="https://twitter.com/bob">@bob</a>');
}
/**
* Test hashtag linking.
*
* @return void
*/
public function testHashtags()
{
$this->visit($this->appurl . '/notes/B')
->see('<a rel="tag" class="p-category" href="/notes/tagged/beer">#beer</a>');
}
/**
* Look for the client name after the note.
*
* @return void
*/
public function testClientNameDisplayed()
{
$this->visit($this->appurl . '/notes/D')
->see('JBL5');
}
/**
* Look for the client URL after the note.
*
* @return void
*/
public function testClientURLDisplayed()
{
$this->visit($this->appurl . '/notes/E')
->see('quill.p3k.io');
}
/**
* Test a correct profile link is formed from a generic URL.
*
* @return void
*/
public function testCreatePhotoLinkWithNonCachedImage()
{
$homepage = 'https://example.org/profile.png';
$expected = 'https://example.org/profile.png';
$this->assertEquals($expected, $this->notesController->createPhotoLink($homepage));
}
/**
* Test a correct profile link is formed from a generic URL.
*
* @return void
*/
public function testCreatePhotoLinkWithCachedImage()
{
$homepage = 'https://aaronparecki.com/profile.png';
$expected = '/assets/profile-images/aaronparecki.com/image';
$this->assertEquals($expected, $this->notesController->createPhotoLink($homepage));
}
/**
* Test a correct profile link is formed from a twitter URL.
*
* @return void
*/
public function testCreatePhotoLinkWithTwimgProfileImageURL()
{
$twitterProfileImage = 'http://pbs.twimg.com/1234';
$expected = 'https://pbs.twimg.com/1234';
$this->assertEquals($expected, $this->notesController->createPhotoLink($twitterProfileImage));
}
/**
* Test `null` is returned for a twitter profile.
*
* @return void
*/
public function testCreatePhotoLinkWithCachedTwitterURL()
{
$twitterURL = 'https://twitter.com/example';
$expected = 'https://pbs.twimg.com/static_profile_link.jpg';
Cache::put($twitterURL, $expected, 1);
$this->assertEquals($expected, $this->notesController->createPhotoLink($twitterURL));
}
}

View file

@ -1,52 +0,0 @@
<?php
namespace App\Tests;
use BrowserKitTest;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class PlacesTest extends BrowserKitTest
{
protected $appurl;
public function setUp()
{
parent::setUp();
$this->appurl = config('app.url');
}
/**
* Test the `/places` page for OK response.
*
* @return void
*/
public function testPlacesPage()
{
$this->visit($this->appurl . '/places')
->assertResponseOK();
}
/**
* Test a specific place.
*
* @return void
*/
public function testSinglePlace()
{
$this->visit($this->appurl . '/places/the-bridgewater-pub')
->see('The Bridgewater Pub');
}
/**
* Test the nearby method returns a collection.
*
* @return void
*/
public function testNearbyMethod()
{
$nearby = \App\Place::near(53.5, -2.38, 1000);
$this->assertEquals('the-bridgewater-pub', $nearby[0]->slug);
}
}

View file

@ -1,25 +1,10 @@
<?php
abstract class TestCase extends Illuminate\Foundation\Testing\TestCase
namespace Tests;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
{
/**
* The base URL to use while testing the application.
*
* @var string
*/
protected $baseUrl = 'http://localhost';
/**
* Creates the application.
*
* @return \Illuminate\Foundation\Application
*/
public function createApplication()
{
$app = require __DIR__.'/../bootstrap/app.php';
$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
return $app;
}
use CreatesApplication;
}

View file

@ -0,0 +1,20 @@
<?php
namespace Tests\Unit;
use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* @return void
*/
public function testBasicTest()
{
$this->assertTrue(true);
}
}

View file

@ -0,0 +1,69 @@
<?php
namespace Tests\Unit;
use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class IndieAuthServiceTest extends TestCase
{
/**
* Test the getAuthorizationEndpoint method.
*
* @return void
*/
public function test_indieauthservice_getauthorizationendpoint_method()
{
$service = new \App\Services\IndieAuthService();
$client = new \IndieAuth\Client();
$result = $service->getAuthorizationEndpoint(config('app.url'), $client);
$this->assertEquals('https://indieauth.com/auth', $result);
}
/**
* Test that the Service build the correct redirect URL.
*
* @return void
*/
public function test_indieauthservice_builds_correct_redirect_url()
{
$service = new \App\Services\IndieAuthService();
$client = new \IndieAuth\Client();
$result = $service->buildAuthorizationURL(
'https://indieauth.com/auth',
config('app.url'),
$client
);
$this->assertEquals(
'https://indieauth.com/auth?me=',
substr($result, 0, 30)
);
}
/**
* Test the getTokenEndpoint method.
*
* @return void
*/
public function test_indieauthservice_gettokenendpoint_method()
{
$service = new \App\Services\IndieAuthService();
$client = new \IndieAuth\Client();
$result = $service->getTokenEndpoint(config('app.url'), $client);
$this->assertEquals(config('app.url') . '/api/token', $result);
}
/**
* Test the discoverMicropubEndpoint method.
*
* @return void
*/
public function test_indieauthservice_discovermicropubendpoint_method()
{
$service = new \App\Services\IndieAuthService();
$client = new \IndieAuth\Client();
$result = $service->discoverMicropubEndpoint(config('app.url'), $client);
$this->assertEquals(config('app.url') . '/api/post', $result);
}
}

View file

@ -0,0 +1,71 @@
<?php
namespace Tests\Unit;
use Tests\TestCase;
use Illuminate\Support\Facades\Cache;
use App\Http\Controllers\NotesController;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class NotesControllerTest extends TestCase
{
protected $notesController;
public function __construct()
{
$this->notesController = new NotesController();
}
/**
* Test a correct profile link is formed from a generic URL.
*
* @return void
*/
public function test_create_photo_link_with_non_cached_image()
{
$notesController = new \App\Http\Controllers\NotesController();
$homepage = 'https://example.org/profile.png';
$expected = 'https://example.org/profile.png';
$this->assertEquals($expected, $this->notesController->createPhotoLink($homepage));
}
/**
* Test a correct profile link is formed from a generic URL (cached).
*
* @return void
*/
public function test_create_photo_link_with_cached_image()
{
$notesController = new \App\Http\Controllers\NotesController();
$homepage = 'https://aaronparecki.com/profile.png';
$expected = '/assets/profile-images/aaronparecki.com/image';
$this->assertEquals($expected, $this->notesController->createPhotoLink($homepage));
}
/**
* Test a correct profile link is formed from a twitter URL.
*
* @return void
*/
public function test_create_photo_link_with_twimg_profile_image_url()
{
$notesController = new \App\Http\Controllers\NotesController();
$twitterProfileImage = 'http://pbs.twimg.com/1234';
$expected = 'https://pbs.twimg.com/1234';
$this->assertEquals($expected, $this->notesController->createPhotoLink($twitterProfileImage));
}
/**
* Test `null` is returned for a twitter profile.
*
* @return void
*/
public function test_create_photo_link_with_cached_twitter_url()
{
$twitterURL = 'https://twitter.com/example';
$expected = 'https://pbs.twimg.com/static_profile_link.jpg';
Cache::put($twitterURL, $expected, 1);
$this->assertEquals($expected, $this->notesController->createPhotoLink($twitterURL));
}
}

70
tests/Unit/NotesTest.php Normal file
View file

@ -0,0 +1,70 @@
<?php
namespace Tests\Unit;
use App\Note;
use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class NotesTest extends TestCase
{
/**
* Test the getNoteAttribute method. This note will check the markdown,
* emoji-a11y, and hashtag sub-methods.
*
* @return void
*/
public function test_get_note_attribute_method()
{
$expected = '<p>Having a <a rel="tag" class="p-category" href="/notes/tagged/beer">#beer</a> at the local. <span role="img" aria-label="beer mug">🍺</span></p>' . PHP_EOL;
$note = Note::find(11);
$this->assertEquals($expected, $note->note);
}
/**
* Look for a default image in the contacts h-card for the makeHCards method.
*
* @return void
*/
public function test_default_image_used_in_makehcards_method()
{
$expected = '<p>Hi <span class="u-category h-card">
<a class="mini-h-card u-url p-name" href="http://tantek.com">
<img class="u-photo" alt="" src="/assets/profile-images/default-image">
Tantek Çelik
</a>
<a class="u-url" href="https://twitter.com/t"></a></span></p>' . PHP_EOL;
$note = Note::find(12);
$this->assertEquals($expected, $note->note);
}
/**
* Look for a specific profile image in the contacts h-card.
*
* @return void
*/
public function test_specific_profile_image_used_in_makehcards_method()
{
$expected = '<p>Hi <span class="u-category h-card">
<a class="mini-h-card u-url p-name" href="https://aaronparecki.com">
<img class="u-photo" alt="" src="/assets/profile-images/aaronparecki.com/image">
Aaron Parecki
</a>
<a class="u-url" href="https://twitter.com/aaronpk"></a></span></p>' . PHP_EOL;
$note = Note::find(13);
$this->assertEquals($expected, $note->note);
}
/**
* Look for twitter URL when theres no associated contact.
*
* @return void
*/
public function test_twitter_link_created_when_no_contact_found()
{
$expected = '<p>Hi <a href="https://twitter.com/bob">@bob</a></p>' . PHP_EOL;
$note = Note::find(14);
$this->assertEquals($expected, $note->note);
}
}

22
tests/Unit/PlacesTest.php Normal file
View file

@ -0,0 +1,22 @@
<?php
namespace Tests\Unit;
use App\Place;
use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class PlacesTest extends TestCase
{
/**
* Test the near method returns a collection.
*
* @return void
*/
public function test_near_method()
{
$nearby = Place::near(53.5, -2.38, 1000);
$this->assertEquals('the-bridgewater-pub', $nearby[0]->slug);
}
}

View file

@ -1,92 +0,0 @@
<?php
namespace App\Tests;
use BrowserKitTest;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class WebMentionsTest extends BrowserKitTest
{
protected $appurl;
public function setUp()
{
parent::setUp();
$this->appurl = config('app.url');
}
/**
* Test webmentions without source and target are rejected.
*
* @return void
*/
public function testWebmentionsWithoutSourceAndTargetAreRejected()
{
$this->call('POST', $this->appurl . '/webmention', ['source' => 'https://example.org/post/123']);
$this->assertResponseStatus(400)
->see('You need both the target and source parameters');
}
/**
* Test invalid target gets a 400 response.
*
* @return void
*/
public function testInvalidTargetReturns400Response()
{
$this->call('POST', $this->appurl . '/webmention', [
'source' => 'https://example.org/post/123',
'target' => $this->appurl . '/invalid/target'
]);
$this->assertResponseStatus(400)
->see('Invalid request');
}
/**
* Test blog target gets a 501 response.
*
* @return void
*/
public function testBlogpostTargetReturns501Response()
{
$this->call('POST', $this->appurl . '/webmention', [
'source' => 'https://example.org/post/123',
'target' => $this->appurl . '/blog/target'
]);
$this->assertResponseStatus(501)
->see('I dont accept webmentions for blog posts yet.');
}
/**
* Test that a non-existant note gives a 400 response.
*
* @return void
*/
public function testNonexistantNoteReturns400Response()
{
$this->call('POST', $this->appurl . '/webmention', [
'source' => 'https://example.org/post/123',
'target' => $this->appurl . '/notes/ZZZZZ'
]);
$this->assertResponseStatus(400)
->see('This note doesnt exist.');
}
/**
* Test a legit webmention triggers the ProcessWebMention job.
*
* @return void
*/
public function testLegitimateWebmnetionTriggersProcessWebMentionJob()
{
$this->expectsJobs(\App\Jobs\ProcessWebMention::class);
$this->call('POST', $this->appurl . '/webmention', [
'source' => 'https://example.org/post/123',
'target' => $this->appurl . '/notes/B'
]);
$this->assertResponseStatus(202)
->see('Webmention received, it will be processed shortly');
}
}

View file

@ -0,0 +1,20 @@
server {
listen 8000 default_server;
listen [::]:8000 default_server ipv6only=on;
root {ROOT}/public;
index index.php;
access_log /tmp/access.log;
error_log /tmp/error.log;
location / {
# First attempt to serve request as file, then as directory, then fall back to index.php.
try_files $uri $uri/ /index.php$is_args$args;
}
location ~* "\.php(/|$)" {
include fastcgi.conf;
fastcgi_pass php;
}
}

39
travis/fastcgi.tpl.conf Normal file
View file

@ -0,0 +1,39 @@
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param HTTPS $https if_not_empty;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200;
fastcgi_split_path_info ^(.+\.php)(.*)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
#fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# fastcgi_intercept_errors on;
fastcgi_ignore_client_abort off;
fastcgi_connect_timeout 60;
fastcgi_send_timeout 1800;
fastcgi_read_timeout 1800;
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
fastcgi_keep_conn on;

44
travis/install-nginx.sh Executable file
View file

@ -0,0 +1,44 @@
#!/bin/bash
set -e
set -x
DIR=$(realpath $(dirname "$0"))
USER=$(whoami)
PHP_VERSION=$(phpenv version-name)
ROOT=$(realpath "$DIR/..")
PORT=9000
SERVER="/tmp/php.sock"
function tpl {
sed \
-e "s|{DIR}|$DIR|g" \
-e "s|{USER}|$USER|g" \
-e "s|{PHP_VERSION}|$PHP_VERSION|g" \
-e "s|{ROOT}|$ROOT|g" \
-e "s|{PORT}|$PORT|g" \
-e "s|{SERVER}|$SERVER|g" \
< $1 > $2
}
# Make some working directories.
mkdir "$DIR/nginx"
mkdir "$DIR/nginx/sites-enabled"
mkdir "$DIR/var"
PHP_FPM_BIN="$HOME/.phpenv/versions/$PHP_VERSION/sbin/php-fpm"
PHP_FPM_CONF="$DIR/nginx/php-fpm.conf"
# Build the php-fpm.conf.
tpl "$DIR/php-fpm.tpl.conf" "$PHP_FPM_CONF"
# Start php-fpm
"$PHP_FPM_BIN" --fpm-config "$PHP_FPM_CONF"
# Build the default nginx config files.
tpl "$DIR/nginx.tpl.conf" "$DIR/nginx/nginx.conf"
tpl "$DIR/fastcgi.tpl.conf" "$DIR/nginx/fastcgi.conf"
tpl "$DIR/default-site.tpl.conf" "$DIR/nginx/sites-enabled/default-site.conf"
# Start nginx.
nginx -c "$DIR/nginx/nginx.conf"

52
travis/nginx.tpl.conf Normal file
View file

@ -0,0 +1,52 @@
error_log /tmp/error.log;
pid /tmp/nginx.pid;
worker_processes 1;
events {
worker_connections 1024;
}
http {
# Set an array of temp and cache file options that will otherwise default to restricted locations accessible only to root.
client_body_temp_path /tmp/client_body;
fastcgi_temp_path /tmp/fastcgi_temp;
proxy_temp_path /tmp/proxy_temp;
scgi_temp_path /tmp/scgi_temp;
uwsgi_temp_path /tmp/uwsgi_temp;
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# Logging Settings
##
access_log /tmp/access.log;
error_log /tmp/error.log;
##
# Gzip Settings
##
gzip on;
gzip_disable "msie6";
##
# Virtual Host Configs
##
# include {DIR}/nginx/conf.d/*.conf;
include {DIR}/nginx/sites-enabled/*;
upstream php {
server 127.0.0.1:{PORT};
}
}

9
travis/php-fpm.tpl.conf Normal file
View file

@ -0,0 +1,9 @@
[global]
[travis]
user = {USER}
listen = {PORT}
listen.mode = 0666
pm = static
pm.max_children = 5
php_admin_value[memory_limit] = 32M