Upgraded to Laravel 5.4

This commit is contained in:
An Phan 2017-02-14 14:53:02 +08:00
parent 38862270db
commit bdca871b6e
No known key found for this signature in database
GPG key ID: 05536BB4BCDC02A2
97 changed files with 3425 additions and 4141 deletions

View file

@ -104,5 +104,5 @@ MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
PUSHER_APP_ID=
PUSHER_KEY=
PUSHER_SECRET=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=

View file

@ -45,14 +45,16 @@ class Application extends IlluminateApplication
{
static $manifest = null;
$manifestFile = $manifestFile ?: $this->publicPath().'/public/build/rev-manifest.json';
$manifestFile = $manifestFile ?: public_path('public/mix-manifest.json');
if ($manifest === null) {
$manifest = json_decode(file_get_contents($manifestFile), true);
}
if (isset($manifest[$file])) {
return $this->staticUrl("public/build/{$manifest[$file]}");
return file_exists(public_path('public/hot'))
? "http://localhost:8080{$manifest[$file]}"
: $this->staticUrl("public{$manifest[$file]}");
}
throw new InvalidArgumentException("File {$file} not defined in asset manifest.");

View file

@ -5,12 +5,15 @@ namespace App\Http;
use App\Http\Middleware\Authenticate;
use App\Http\Middleware\GetUserFromToken;
use App\Http\Middleware\ObjectStorageAuthenticate;
use App\Http\Middleware\TrimStrings;
use App\Http\Middleware\UseDifferentConfigIfE2E;
use Illuminate\Auth\Middleware\Authorize;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
class Kernel extends HttpKernel
{
@ -22,6 +25,9 @@ class Kernel extends HttpKernel
protected $middleware = [
CheckForMaintenanceMode::class,
UseDifferentConfigIfE2E::class,
ValidatePostSize::class,
TrimStrings::class,
ConvertEmptyStringsToNull::class,
];
/**

View file

@ -3,8 +3,8 @@
namespace App\Http\Middleware;
use App\JWTAuth;
use Illuminate\Events\Dispatcher;
use Illuminate\Routing\ResponseFactory;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Routing\ResponseFactory;
use Tymon\JWTAuth\Middleware\BaseMiddleware as JWTBaseMiddleware;
abstract class BaseMiddleware extends JWTBaseMiddleware

View file

@ -0,0 +1,18 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\TrimStrings as BaseTrimmer;
class TrimStrings extends BaseTrimmer
{
/**
* The names of the attributes that should not be trimmed.
*
* @var array
*/
protected $except = [
'password',
'password_confirmation',
];
}

View file

@ -15,7 +15,7 @@ class JWTAuth extends BaseJWTAuth
*/
public function __construct(JWTManager $manager, UserInterface $user, AuthInterface $auth, Request $request)
{
return parent::__construct($manager, $user, $auth, $request);
parent::__construct($manager, $user, $auth, $request);
}
/**

View file

@ -16,11 +16,6 @@ class BroadcastServiceProvider extends ServiceProvider
{
Broadcast::routes();
/*
* Authenticate the user's personal channel...
*/
Broadcast::channel('App.User.*', function ($user, $userId) {
return (int) $user->id === (int) $userId;
});
require base_path('routes/channels.php');
}
}

View file

@ -47,12 +47,9 @@ class RouteServiceProvider extends ServiceProvider
*/
protected function mapWebRoutes()
{
Route::group([
'middleware' => 'web',
'namespace' => $this->namespace,
], function ($router) {
require base_path('routes/web.php');
});
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}
/**
@ -64,12 +61,9 @@ class RouteServiceProvider extends ServiceProvider
*/
protected function mapApiRoutes()
{
Route::group([
'middleware' => 'api',
'namespace' => $this->namespace,
'prefix' => 'api',
], function ($router) {
require base_path('routes/api.php');
});
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
}
}

View file

@ -15,20 +15,3 @@ define('LARAVEL_START', microtime(true));
*/
require __DIR__.'/../vendor/autoload.php';
/*
|--------------------------------------------------------------------------
| Include The Compiled Class File
|--------------------------------------------------------------------------
|
| To dramatically increase your application's performance, you may use a
| compiled class file which contains all of the classes commonly used
| by a request. The Artisan "optimize" is used to create this file.
|
*/
$compiledPath = __DIR__.'/cache/compiled.php';
if (file_exists($compiledPath)) {
require $compiledPath;
}

View file

@ -6,7 +6,7 @@
"type": "project",
"require": {
"php": ">=5.6.4",
"laravel/framework": "5.3.*",
"laravel/framework": "5.4.*",
"james-heinrich/getid3": "^1.9",
"guzzlehttp/guzzle": "^6.1",
"tymon/jwt-auth": "^0.5.6",
@ -17,24 +17,27 @@
"require-dev": {
"fzaninotto/faker": "~1.4",
"mockery/mockery": "0.9.*",
"phpunit/phpunit": "~4.0",
"phpspec/phpspec": "~2.1",
"phpunit/phpunit": "~5.7",
"symfony/css-selector": "2.8.*|3.0.*",
"symfony/dom-crawler": "2.8.*|3.0.*",
"symfony/dom-crawler": "^3.2",
"facebook/webdriver": "^1.2",
"barryvdh/laravel-ide-helper": "^2.1"
"barryvdh/laravel-ide-helper": "^2.1",
"laravel/tinker": "^1.0",
"laravel/browser-kit-testing": "^1.0"
},
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"App\\": "app/"
"App\\": "app/",
"Tests\\": "tests/"
}
},
"autoload-dev": {
"classmap": [
"tests/TestCase.php",
"tests/BrowserKitTestCase.php",
"tests/e2e"
]
},

1777
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -138,7 +138,7 @@ return [
Illuminate\Translation\TranslationServiceProvider::class,
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
Laravel\Tinker\TinkerServiceProvider::class,
Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,
Tymon\JWTAuth\Providers\JWTAuthServiceProvider::class,
Aws\Laravel\AwsServiceProvider::class,

View file

@ -30,8 +30,8 @@ return [
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_KEY'),
'secret' => env('PUSHER_SECRET'),
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
//

View file

@ -44,7 +44,7 @@ return [
'file' => [
'driver' => 'file',
'path' => storage_path('framework/cache'),
'path' => storage_path('framework/cache/data'),
],
'memcached' => [

View file

@ -2,19 +2,6 @@
return [
/*
|--------------------------------------------------------------------------
| PDO Fetch Style
|--------------------------------------------------------------------------
|
| By default, database results will be returned as instances of the PHP
| stdClass object; however, you may desire to retrieve records in an
| array format for simplicity. Here you can tweak the fetch style.
|
*/
'fetch' => PDO::FETCH_CLASS,
/*
|--------------------------------------------------------------------------
| Default Database Connection Name
@ -64,8 +51,8 @@ return [
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'strict' => false,
],
@ -119,12 +106,21 @@ return [
'redis' => [
'cluster' => false,
'client' => 'predis',
'default' => [
'host' => '127.0.0.1',
'port' => 6379,
'database' => 0,
'options' => [
'cluster' => 'redis',
],
'clusters' => [
'default' => [
[
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => 0,
],
],
],
],

View file

@ -64,10 +64,10 @@ return [
's3' => [
'driver' => 's3',
'key' => 'your-key',
'secret' => 'your-secret',
'region' => 'your-region',
'bucket' => 'your-bucket',
'key' => env('AWS_KEY'),
'secret' => env('AWS_SECRET'),
'region' => env('AWS_REGION'),
'bucket' => env('AWS_BUCKET'),
],
'rackspace' => [

View file

@ -1,7 +1,5 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Mail Driver
@ -11,12 +9,11 @@ return [
| sending of e-mail. You may specify which one you're using throughout
| your application here. By default, Laravel is setup for SMTP mail.
|
| Supported: "smtp", "mail", "sendmail", "mailgun", "mandrill", "ses", "log"
| Supported: "smtp", "sendmail", "mailgun", "mandrill", "ses",
| "sparkpost", "log", "array"
|
*/
'driver' => env('MAIL_DRIVER', 'smtp'),
/*
|--------------------------------------------------------------------------
| SMTP Host Address
@ -27,9 +24,7 @@ return [
| the Mailgun mail service which will provide reliable deliveries.
|
*/
'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
/*
|--------------------------------------------------------------------------
| SMTP Host Port
@ -40,9 +35,7 @@ return [
| stay compatible with the Mailgun e-mail application by default.
|
*/
'port' => env('MAIL_PORT', 587),
/*
|--------------------------------------------------------------------------
| Global "From" Address
@ -53,9 +46,10 @@ return [
| used globally for all e-mails that are sent by your application.
|
*/
'from' => ['address' => null, 'name' => null],
'from' => [
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
'name' => env('MAIL_FROM_NAME', 'Example'),
],
/*
|--------------------------------------------------------------------------
| E-Mail Encryption Protocol
@ -66,9 +60,7 @@ return [
| transport layer security protocol should provide great security.
|
*/
'encryption' => env('MAIL_ENCRYPTION', 'tls'),
/*
|--------------------------------------------------------------------------
| SMTP Server Username
@ -79,22 +71,8 @@ return [
| connection. You may also set the "password" value below this one.
|
*/
'username' => env('MAIL_USERNAME'),
/*
|--------------------------------------------------------------------------
| SMTP Server Password
|--------------------------------------------------------------------------
|
| Here you may set the password required by your SMTP server to send out
| messages from your application. This will be given to the server on
| connection so that the application will be able to send messages.
|
*/
'password' => env('MAIL_PASSWORD'),
/*
|--------------------------------------------------------------------------
| Sendmail System Path
@ -105,7 +83,21 @@ return [
| been provided here, which will work well on most of your systems.
|
*/
'sendmail' => '/usr/sbin/sendmail -bs',
/*
|--------------------------------------------------------------------------
| Markdown Mail Settings
|--------------------------------------------------------------------------
|
| If you are using Markdown based email rendering, you may configure your
| theme and component paths here, allowing you to customize the design
| of the emails. Or, you may simply stick with the Laravel defaults!
|
*/
'markdown' => [
'theme' => 'default',
'paths' => [
resource_path('views/vendor/mail'),
],
],
];

View file

@ -14,8 +14,7 @@
"url": "https://github.com/phanan/koel"
},
"babel": {
"presets": ["es2015"],
"plugins": ["lodash"]
"presets": ["es2015"]
},
"eslintConfig": {
"extends": "vue",
@ -27,15 +26,13 @@
"alertify.js": "^1.0.12",
"axios": "^0.15.3",
"blueimp-md5": "^2.3.0",
"font-awesome": "^4.5.0",
"intersection-observer": "^0.2.0",
"ismobilejs": "^0.4.0",
"local-storage": "^1.4.2",
"lodash": "^4.6.1",
"lodash": "^4.16.2",
"nouislider": "^9.1.0",
"nprogress": "^0.2.0",
"plyr": "1.5.x",
"raven-js": "^3.9.1",
"select": "^1.0.6",
"slugify": "^1.0.2",
"vue": "^2.1.9",
@ -44,37 +41,30 @@
"youtube-player": "^3.0.4"
},
"devDependencies": {
"babel-plugin-lodash": "^2.2.1",
"babel-plugin-transform-runtime": "^6.3.13",
"babel-polyfill": "^6.9.1",
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-register": "^6.3.13",
"babel-runtime": "^6.0.0",
"browserify-hmr": "^0.3.1",
"autoprefixer": "^6.7.2",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-polyfill": "^6.23.0",
"babel-register": "^6.23.0",
"babel-runtime": "^6.22.0",
"chai": "^3.4.1",
"chalk": "^1.1.3",
"cross-env": "^1.0.7",
"cross-env": "^3.1.4",
"eslint": "^3.10.2",
"eslint-config-vue": "^2.0.1",
"eslint-plugin-vue": "^1.0.0",
"gulp": "^3.9.0",
"gulp-util": "^3.0.7",
"font-awesome": "^4.7.0",
"jsdom": "^9.2.1",
"laravel-elixir": "^5.0.0",
"laravel-mix": "^0.7.4",
"mocha": "^2.3.4",
"node-sass": "^3.4.2",
"postcss-cssnext": "^2.6.0",
"sinon": "^1.17.2",
"vue-hot-reload-api": "^1.3.2",
"vueify": "^9.1.0",
"vueify-insert-css": "^1.0.0"
"sinon": "^1.17.2"
},
"scripts": {
"postinstall": "cross-env NODE_ENV=production && gulp --production",
"build": "cross-env NODE_ENV=production && gulp --production",
"postinstall": "yarn build",
"test": "eslint resources/assets/js --ext=js,vue && mocha --compilers js:babel-register --require resources/assets/js/tests/helper.js resources/assets/js/tests/**/*Test.js",
"e2e": "gulp e2e",
"dev": "cross-env NODE_ENV=development && gulp watch"
"dev": "node node_modules/cross-env/bin/cross-env.js NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules",
"watch": "node node_modules/cross-env/bin/cross-env.js NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules",
"hot": "node node_modules/cross-env/bin/cross-env.js NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot",
"build": "node node_modules/cross-env/bin/cross-env.js NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules"
}
}

View file

@ -1,5 +0,0 @@
suites:
main:
namespace: App
psr4_prefix: App
src_path: app

View file

@ -9,14 +9,17 @@
processIsolation="false"
stopOnFailure="false">
<testsuites>
<testsuite name="Application Test Suite">
<directory>./tests/</directory>
<exclude>./tests/e2e</exclude>
<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>
<directory suffix=".php">app/</directory>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./app/</directory>
</whitelist>
</filter>
<php>

View file

@ -1,10 +1,8 @@
require('intersection-observer')
import './static-loader'
import Vue from 'vue'
import { VirtualScroller } from 'vue-virtual-scroller'
import { event } from './utils'
import { http } from './services'
import { VirtualScroller } from 'vue-virtual-scroller/dist/vue-virtual-scroller'
Vue.component('virtual-scroller', VirtualScroller)
/**

View file

@ -229,7 +229,7 @@ Vue.directive('koel-focus', focusDirective)
Vue.directive('koel-clickaway', clickawayDirective)
</script>
<style lang="sass">
<style lang="scss">
@import "resources/assets/sass/partials/_vars.scss";
@import "resources/assets/sass/partials/_mixins.scss";
@import "resources/assets/sass/partials/_shared.scss";

View file

@ -38,7 +38,7 @@ export default {
}
</script>
<style lang="sass" scoped>
<style lang="scss" scoped>
@import "../../../sass/partials/_vars.scss";
@import "../../../sass/partials/_mixins.scss";
@import "../../../sass/partials/_shared.scss";

View file

@ -88,7 +88,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../../sass/partials/_vars.scss";
@import "../../../../sass/partials/_mixins.scss";

View file

@ -71,7 +71,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../../sass/partials/_vars.scss";
@import "../../../../sass/partials/_mixins.scss";

View file

@ -116,7 +116,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../../sass/partials/_vars.scss";
@import "../../../../sass/partials/_mixins.scss";

View file

@ -13,7 +13,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../../sass/partials/_vars.scss";
@import "../../../../sass/partials/_mixins.scss";
</style>

View file

@ -56,7 +56,7 @@ export default {
}
</script>
<style lang="sass" scoped>
<style lang="scss" scoped>
#youtube-extra-wrapper {
overflow-x: hidden;

View file

@ -16,7 +16,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../sass/partials/_vars.scss";
@import "../../../sass/partials/_mixins.scss";

View file

@ -147,7 +147,7 @@ export default {
}
</script>
<style lang="sass" scoped>
<style lang="scss" scoped>
@import "../../../../sass/partials/_vars.scss";
@import "../../../../sass/partials/_mixins.scss";

View file

@ -63,7 +63,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../../sass/partials/_vars.scss";
@import "../../../../sass/partials/_mixins.scss";

View file

@ -139,7 +139,7 @@ export default {
}
</script>
<style lang="sass" scoped>
<style lang="scss" scoped>
@import "../../../../sass/partials/_vars.scss";
@import "../../../../sass/partials/_mixins.scss";

View file

@ -65,7 +65,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../../sass/partials/_vars.scss";
@import "../../../../sass/partials/_mixins.scss";

View file

@ -65,7 +65,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../../sass/partials/_vars.scss";
@import "../../../../sass/partials/_mixins.scss";

View file

@ -154,7 +154,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../../sass/partials/_vars.scss";
@import "../../../../sass/partials/_mixins.scss";

View file

@ -68,7 +68,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../../sass/partials/_vars.scss";
@import "../../../../sass/partials/_mixins.scss";

View file

@ -128,7 +128,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../../sass/partials/_vars.scss";
@import "../../../../sass/partials/_mixins.scss";

View file

@ -180,7 +180,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../../sass/partials/_vars.scss";
@import "../../../../sass/partials/_mixins.scss";

View file

@ -81,7 +81,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../../sass/partials/_vars.scss";
@import "../../../../sass/partials/_mixins.scss";

View file

@ -84,7 +84,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../../sass/partials/_vars.scss";
@import "../../../../sass/partials/_mixins.scss";

View file

@ -42,7 +42,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../../sass/partials/_vars.scss";
@import "../../../../sass/partials/_mixins.scss";
</style>

View file

@ -64,7 +64,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../../sass/partials/_vars.scss";
@import "../../../../sass/partials/_mixins.scss";

View file

@ -56,7 +56,7 @@ export default {
}
</script>
<style lang="sass" scoped>
<style lang="scss" scoped>
@import "../../../../sass/partials/_vars.scss";
@import "../../../../sass/partials/_mixins.scss";

View file

@ -149,7 +149,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../../sass/partials/_vars.scss";
@import "../../../../sass/partials/_mixins.scss";

View file

@ -155,7 +155,7 @@ export default {
}
</script>
<style lang="sass" scoped>
<style lang="scss" scoped>
@import "../../../../sass/partials/_vars.scss";
@import "../../../../sass/partials/_mixins.scss";

View file

@ -58,7 +58,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../../sass/partials/_vars.scss";
@import "../../../../sass/partials/_mixins.scss";

View file

@ -322,7 +322,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../sass/partials/_vars.scss";
@import "../../../sass/partials/_mixins.scss";

View file

@ -79,7 +79,7 @@ export default {
}
</script>
<style lang="sass" scoped>
<style lang="scss" scoped>
@import "../../../sass/partials/_vars.scss";
@import "../../../sass/partials/_mixins.scss";

View file

@ -102,7 +102,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../sass/partials/_vars.scss";
@import "../../../sass/partials/_mixins.scss";

View file

@ -91,7 +91,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../sass/partials/_vars.scss";
@import "../../../sass/partials/_mixins.scss";

View file

@ -55,7 +55,7 @@ export default {
}
</script>
<style lang="sass" scoped>
<style lang="scss" scoped>
@import "../../../sass/partials/_vars.scss";
@import "../../../sass/partials/_mixins.scss";

View file

@ -80,7 +80,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../sass/partials/_vars.scss";
@import "../../../sass/partials/_mixins.scss";

View file

@ -141,7 +141,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../sass/partials/_vars.scss";
@import "../../../sass/partials/_mixins.scss";

View file

@ -26,7 +26,7 @@ export default {
}
</script>
<style lang="sass" scoped>
<style lang="scss" scoped>
@import "../../../sass/partials/_vars.scss";
@import "../../../sass/partials/_mixins.scss";

View file

@ -104,4 +104,4 @@ export default {
}
</script>
<style lang="sass"></style>
<style lang="scss"></style>

View file

@ -433,7 +433,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../sass/partials/_vars.scss";
@import "../../../sass/partials/_mixins.scss";

View file

@ -175,7 +175,7 @@ export default {
}
</script>
<style lang="sass" scoped>
<style lang="scss" scoped>
@import "../../../sass/partials/_vars.scss";
@import "../../../sass/partials/_mixins.scss";

View file

@ -45,7 +45,7 @@ export default {
}
</script>
<style lang="sass" scoped>
<style lang="scss" scoped>
.bars {
width: 28px;
height: 13px;

View file

@ -35,7 +35,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../sass/partials/_vars.scss";
@import "../../../sass/partials/_mixins.scss";

View file

@ -54,7 +54,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
a.view-on-itunes {
display: inline-block;
border-radius: 3px;

View file

@ -155,7 +155,7 @@ export default {
}
</script>
<style lang="sass" scoped>
<style lang="scss" scoped>
@import "../../../sass/partials/_vars.scss";
@import "../../../sass/partials/_mixins.scss";

View file

@ -73,7 +73,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../sass/partials/_vars.scss";
@import "../../../sass/partials/_mixins.scss";
</style>

View file

@ -59,7 +59,7 @@ export default {
}
</script>
<style lang="sass" scoped>
<style lang="scss" scoped>
@import "../../../sass/partials/_vars.scss";
@import "../../../sass/partials/_mixins.scss";

View file

@ -224,7 +224,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../sass/partials/_vars.scss";
@import "../../../sass/partials/_mixins.scss";

View file

@ -198,7 +198,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../sass/partials/_vars.scss";
@import "../../../sass/partials/_mixins.scss";

View file

@ -36,7 +36,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../sass/partials/_vars.scss";
@import "../../../sass/partials/_mixins.scss";

View file

@ -46,7 +46,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../sass/partials/_vars.scss";
@import "../../../sass/partials/_mixins.scss";

View file

@ -43,7 +43,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../sass/partials/_vars.scss";
@import "../../../sass/partials/_mixins.scss";

View file

@ -30,7 +30,7 @@ export default {
}
</script>
<style lang="sass">
<style lang="scss">
@import "../../../sass/partials/_vars.scss";
@import "../../../sass/partials/_mixins.scss";

View file

@ -0,0 +1,7 @@
import 'babel-polyfill/dist/polyfill.min.js'
import 'plyr/dist/plyr.js'
import './libs/modernizr-custom.js'
import '../css/meyer-reset.min.css'
import 'nouislider/distribute/nouislider.min.css'
import 'intersection-observer'
import 'font-awesome/css/font-awesome.min.css'

View file

@ -17,14 +17,12 @@
<link rel="icon" href="{{ App::staticUrl('public/img/icon.png') }}">
<link rel="apple-touch-icon" href="{{ App::staticUrl('public/img/icon.png') }}">
<link rel="stylesheet" href="{{ App::rev('css/vendors.css') }}">
<link rel="stylesheet" href="{{ App::rev('css/app.css') }}">
<link rel="stylesheet" href="{{ App::rev('/css/app.css') }}">
</head>
<body>
<div id="app"></div>
<noscript>It may sound funny, but Koel requires JavaScript to sing. Please enable it.</noscript>
<script src="{{ App::rev('js/vendors.js') }}"></script>
<script src="{{ App::rev('js/main.js') }}"></script>
<script src="{{ App::rev('/js/app.js') }}"></script>
</body>
</html>

17
routes/channels.php Normal file
View file

@ -0,0 +1,17 @@
<?php
use Illuminate\Support\Facades\Broadcast;
/*
|--------------------------------------------------------------------------
| Broadcast Channels
|--------------------------------------------------------------------------
|
| Here you may register all of the event broadcasting channels that your
| application supports. The given channel authorization callbacks are
| used to check if an authenticated user can listen to the channel.
|
*/
Broadcast::channel('App.User.{id}', function ($user, $id) {
return (int) $user->id === (int) $id;
});

View file

@ -1,47 +0,0 @@
<?php
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;
class ApplicationTest extends TestCase
{
public function testStaticUrlWithoutCDN()
{
config(['koel.cdn.url' => '']);
$this->assertEquals(App::staticUrl(), 'http://localhost/');
$this->assertEquals(App::staticUrl('foo.css '), 'http://localhost/foo.css');
}
public function testStaticUrlWithCDN()
{
config(['koel.cdn.url' => 'http://cdn.bar']);
$this->assertEquals(App::staticUrl(), 'http://cdn.bar/');
$this->assertEquals(App::staticUrl('foo.css '), 'http://cdn.bar/foo.css');
}
public function testRev()
{
config(['koel.cdn.url' => '']);
$manifestFile = __DIR__.'/blobs/rev-manifest.json';
$this->assertEquals(App::rev('foo.css', $manifestFile), 'http://localhost/public/build/foo00.css');
config(['koel.cdn.url' => 'http://cdn.bar']);
$this->assertEquals(App::rev('bar.js', $manifestFile), 'http://cdn.bar/public/build/bar00.js');
}
public function testGetLatestVersion()
{
$mock = new MockHandler([
new Response(200, [], file_get_contents(__DIR__.'/blobs/github-tags.json')),
]);
$client = new Client(['handler' => HandlerStack::create($mock)]);
$this->assertEquals('v1.1.2', App::getLatestVersion($client));
}
}

View file

@ -0,0 +1,131 @@
<?php
namespace Tests;
use App\Models\Album;
use App\Models\Artist;
use App\Models\Song;
use App\Models\User;
use Illuminate\Contracts\Console\Kernel;
use Illuminate\Support\Facades\Artisan;
use JWTAuth;
use Laravel\BrowserKitTesting\TestCase as BaseBrowserKitTestCase;
abstract class BrowserKitTestCase extends BaseBrowserKitTestCase
{
protected $mediaPath;
protected $coverPath;
public function __construct()
{
parent::__construct();
$this->mediaPath = __DIR__.'/songs';
}
public function setUp()
{
parent::setUp();
$this->prepareForTests();
}
/**
* 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(Kernel::class)->bootstrap();
$this->coverPath = $app->basePath().'/public/img/covers';
return $app;
}
private function prepareForTests()
{
Artisan::call('migrate');
if (!User::all()->count()) {
Artisan::call('db:seed');
}
if (!file_exists($this->coverPath)) {
mkdir($this->coverPath, 0777, true);
}
}
/**
* Create a sample media set, with a complete artist+album+song trio.
*/
protected function createSampleMediaSet()
{
$artist = factory(Artist::class)->create();
// Sample 3 albums
$albums = factory(Album::class, 3)->create([
'artist_id' => $artist->id,
]);
// 7-15 songs per albums
foreach ($albums as $album) {
factory(Song::class, random_int(7, 15))->create([
'album_id' => $album->id,
]);
}
}
protected function getAsUser($url, $user = null)
{
if (!$user) {
$user = factory(User::class)->create();
}
return $this->get($url, [
'Authorization' => 'Bearer '.JWTAuth::fromUser($user),
]);
}
protected function deleteAsUser($url, $data = [], $user = null)
{
if (!$user) {
$user = factory(User::class)->create();
}
return $this->delete($url, $data, [
'Authorization' => 'Bearer '.JWTAuth::fromUser($user),
]);
}
protected function postAsUser($url, $data, $user = null)
{
if (!$user) {
$user = factory(User::class)->create();
}
return $this->post($url, $data, [
'Authorization' => 'Bearer '.JWTAuth::fromUser($user),
]);
}
protected function putAsUser($url, $data, $user = null)
{
if (!$user) {
$user = factory(User::class)->create();
}
return $this->put($url, $data, [
'Authorization' => 'Bearer '.JWTAuth::fromUser($user),
]);
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace Tests;
use Illuminate\Contracts\Console\Kernel;
trait CreatesApplication
{
protected $coverPath;
/**
* 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(Kernel::class)->bootstrap();
$this->coverPath = $app->basePath().'/public/img/covers';
return $app;
}
}

View file

@ -0,0 +1,57 @@
<?php
namespace Tests\Feature;
use App;
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;
use Tests\BrowserKitTestCase;
class ApplicationTest extends BrowserKitTestCase
{
public function setUp()
{
parent::setUp();
@unlink(App::publicPath().'/public/hot');
}
public function testStaticUrlWithoutCDN()
{
config(['koel.cdn.url' => '']);
$this->assertEquals('http://localhost/', App::staticUrl());
$this->assertEquals('http://localhost/foo.css', App::staticUrl('/foo.css '));
}
public function testStaticUrlWithCDN()
{
config(['koel.cdn.url' => 'http://cdn.bar']);
$this->assertEquals('http://cdn.bar/', App::staticUrl());
$this->assertEquals('http://cdn.bar/foo.css', App::staticUrl('/foo.css '));
}
public function testRev()
{
config(['koel.cdn.url' => '']);
$manifestFile = __DIR__.'../../blobs/rev-manifest.json';
$this->assertEquals('http://localhost/public/foo00.css', App::rev('/foo.css', $manifestFile));
config(['koel.cdn.url' => 'http://cdn.bar']);
$this->assertEquals('http://cdn.bar/public/bar00.js', App::rev('/bar.js', $manifestFile));
}
public function testGetLatestVersion()
{
$mock = new MockHandler([
new Response(200, [], file_get_contents(__DIR__.'../../blobs/github-tags.json')),
]);
$client = new Client(['handler' => HandlerStack::create($mock)]);
$this->assertEquals('v1.1.2', App::getLatestVersion($client));
}
}

View file

@ -1,9 +1,12 @@
<?php
namespace Tests\Feature;
use App\Models\Artist;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\BrowserKitTestCase;
class ArtistTest extends TestCase
class ArtistTest extends BrowserKitTestCase
{
use DatabaseTransactions;
@ -30,7 +33,7 @@ class ArtistTest extends TestCase
public function testUtf16Names()
{
$name = file_get_contents(__DIR__.'/blobs/utf16');
$name = file_get_contents(__DIR__.'../../blobs/utf16');
$artist = Artist::get($name);
$artist = Artist::get($name); // to make sure there's no constraint exception

View file

@ -1,13 +1,17 @@
<?php
namespace Tests\Feature;
use App\Models\Album;
use App\Models\Artist;
use App\Models\Playlist;
use App\Models\Song;
use App\Models\User;
use Download;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\BrowserKitTestCase;
class DownloadTest extends TestCase
class DownloadTest extends BrowserKitTestCase
{
use DatabaseTransactions;

View file

@ -1,12 +1,15 @@
<?php
namespace Tests\Feature;
use App\Events\SongLikeToggled;
use App\Models\Song;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Tests\BrowserKitTestCase;
class InteractionTest extends TestCase
class InteractionTest extends BrowserKitTestCase
{
use DatabaseTransactions, WithoutMiddleware;
@ -22,8 +25,7 @@ class InteractionTest extends TestCase
$user = factory(User::class)->create();
$song = Song::orderBy('id')->first();
$this->actingAs($user)
->post('api/interaction/play', ['song' => $song->id]);
$this->postAsUser('api/interaction/play', ['song' => $song->id], $user);
$this->seeInDatabase('interactions', [
'user_id' => $user->id,
@ -32,8 +34,7 @@ class InteractionTest extends TestCase
]);
// Try again
$this->actingAs($user)
->post('api/interaction/play', ['song' => $song->id]);
$this->postAsUser('api/interaction/play', ['song' => $song->id], $user);
$this->seeInDatabase('interactions', [
'user_id' => $user->id,
@ -49,8 +50,7 @@ class InteractionTest extends TestCase
$user = factory(User::class)->create();
$song = Song::orderBy('id')->first();
$this->actingAs($user)
->post('api/interaction/like', ['song' => $song->id]);
$this->postAsUser('api/interaction/like', ['song' => $song->id], $user);
$this->seeInDatabase('interactions', [
'user_id' => $user->id,
@ -59,8 +59,7 @@ class InteractionTest extends TestCase
]);
// Try again
$this->actingAs($user)
->post('api/interaction/like', ['song' => $song->id]);
$this->postAsUser('api/interaction/like', ['song' => $song->id], $user);
$this->seeInDatabase('interactions', [
'user_id' => $user->id,
@ -78,8 +77,7 @@ class InteractionTest extends TestCase
$songs = Song::orderBy('id')->take(2)->get();
$songIds = array_pluck($songs->toArray(), 'id');
$this->actingAs($user)
->post('api/interaction/batch/like', ['songs' => $songIds]);
$this->postAsUser('api/interaction/batch/like', ['songs' => $songIds], $user);
foreach ($songs as $song) {
$this->seeInDatabase('interactions', [
@ -89,8 +87,7 @@ class InteractionTest extends TestCase
]);
}
$this->actingAs($user)
->post('api/interaction/batch/unlike', ['songs' => $songIds]);
$this->postAsUser('api/interaction/batch/unlike', ['songs' => $songIds], $user);
foreach ($songs as $song) {
$this->seeInDatabase('interactions', [

View file

@ -1,5 +1,7 @@
<?php
namespace Tests\Feature;
use App\Events\SongLikeToggled;
use App\Events\SongStartedPlaying;
use App\Http\Controllers\API\LastfmController;
@ -17,16 +19,17 @@ use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Mockery as m;
use Tests\BrowserKitTestCase;
use Tymon\JWTAuth\JWTAuth;
class LastfmTest extends TestCase
class LastfmTest extends BrowserKitTestCase
{
use DatabaseTransactions, WithoutMiddleware;
public function testGetArtistInfo()
{
$client = m::mock(Client::class, [
'get' => new Response(200, [], file_get_contents(__DIR__.'/blobs/lastfm/artist.xml')),
'get' => new Response(200, [], file_get_contents(__DIR__.'../../blobs/lastfm/artist.xml')),
]);
$api = new Lastfm(null, null, $client);
@ -47,7 +50,7 @@ class LastfmTest extends TestCase
public function testGetArtistInfoFailed()
{
$client = m::mock(Client::class, [
'get' => new Response(400, [], file_get_contents(__DIR__.'/blobs/lastfm/artist-notfound.xml')),
'get' => new Response(400, [], file_get_contents(__DIR__.'../../blobs/lastfm/artist-notfound.xml')),
]);
$api = new Lastfm(null, null, $client);
@ -58,7 +61,7 @@ class LastfmTest extends TestCase
public function testGetAlbumInfo()
{
$client = m::mock(Client::class, [
'get' => new Response(200, [], file_get_contents(__DIR__.'/blobs/lastfm/album.xml')),
'get' => new Response(200, [], file_get_contents(__DIR__.'../../blobs/lastfm/album.xml')),
]);
$api = new Lastfm(null, null, $client);
@ -91,7 +94,7 @@ class LastfmTest extends TestCase
public function testGetAlbumInfoFailed()
{
$client = m::mock(Client::class, [
'get' => new Response(400, [], file_get_contents(__DIR__.'/blobs/lastfm/album-notfound.xml')),
'get' => new Response(400, [], file_get_contents(__DIR__.'../../blobs/lastfm/album-notfound.xml')),
]);
$api = new Lastfm(null, null, $client);
@ -121,7 +124,7 @@ class LastfmTest extends TestCase
public function testGetSessionKey()
{
$client = m::mock(Client::class, [
'get' => new Response(200, [], file_get_contents(__DIR__.'/blobs/lastfm/session-key.xml')),
'get' => new Response(200, [], file_get_contents(__DIR__.'../../blobs/lastfm/session-key.xml')),
]);
$api = new Lastfm(null, null, $client);
@ -132,7 +135,8 @@ class LastfmTest extends TestCase
public function testSetSessionKey()
{
$user = factory(User::class)->create();
$this->actingAs($user)->post('api/lastfm/session-key', ['key' => 'foo']);
$this->postAsUser('api/lastfm/session-key', ['key' => 'foo'], $user);
$user = User::find($user->id);
$this->assertEquals('foo', $user->lastfm_session_key);
}
@ -166,7 +170,8 @@ class LastfmTest extends TestCase
public function testControllerDisconnect()
{
$user = factory(User::class)->create(['preferences' => ['lastfm_session_key' => 'bar']]);
$this->actingAs($user)->delete('api/lastfm/disconnect');
$this->deleteAsUser('api/lastfm/disconnect', [], $user);
$user = User::find($user->id);
$this->assertNull($user->lastfm_session_key);
}

View file

@ -1,5 +1,7 @@
<?php
namespace Tests\Feature;
use App\Events\LibraryChanged;
use App\Libraries\WatchRecord\InotifyWatchRecord;
use App\Models\Album;
@ -10,8 +12,9 @@ use App\Services\Media;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Mockery as m;
use Tests\BrowserKitTestCase;
class MediaTest extends TestCase
class MediaTest extends BrowserKitTestCase
{
use DatabaseTransactions, WithoutMiddleware;

View file

@ -1,13 +1,22 @@
<?php
namespace Tests\Feature\ObjectStorage;
use App\Events\LibraryChanged;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Tests\BrowserKitTestCase;
class ObjectStorage_S3Test extends TestCase
class S3Test extends BrowserKitTestCase
{
use DatabaseTransactions, WithoutMiddleware;
public function setUp()
{
parent::setUp();
$this->withoutMiddleware();
}
public function testPut()
{
$this->post('api/os/s3/song', [
@ -27,7 +36,6 @@ class ObjectStorage_S3Test extends TestCase
public function testRemove()
{
$this->expectsEvents(LibraryChanged::class);
$this->post('api/os/s3/song', [
'bucket' => 'koel',
'key' => 'sample.mp3',

View file

@ -1,11 +1,14 @@
<?php
namespace Tests\Feature;
use App\Models\Playlist;
use App\Models\Song;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\BrowserKitTestCase;
class PlaylistTest extends TestCase
class PlaylistTest extends BrowserKitTestCase
{
use DatabaseTransactions;

View file

@ -1,17 +1,20 @@
<?php
namespace Tests\Feature;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Tests\BrowserKitTestCase;
class ProfileTest extends TestCase
class ProfileTest extends BrowserKitTestCase
{
use WithoutMiddleware, DatabaseTransactions;
public function testUpdate()
{
$this->actingAs(factory(User::class)->create())
->put('api/me', ['name' => 'Foo', 'email' => 'bar@baz.com']);
$user = factory(User::class)->create();
$this->putAsUser('api/me', ['name' => 'Foo', 'email' => 'bar@baz.com'], $user);
$this->seeInDatabase('users', ['name' => 'Foo', 'email' => 'bar@baz.com']);
}

View file

@ -1,13 +1,16 @@
<?php
namespace Tests\Feature;
use App\Services\RESTfulService;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Response;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Mockery as m;
use Tests\BrowserKitTestCase;
class RESTfulAPIServiceTest extends TestCase
class RESTfulAPIServiceTest extends BrowserKitTestCase
{
use DatabaseTransactions, WithoutMiddleware;

View file

@ -1,12 +1,15 @@
<?php
namespace Tests\Feature;
use App\Models\Song;
use App\Services\Lastfm;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Mockery as m;
use Tests\BrowserKitTestCase;
class ScrobbleTest extends TestCase
class ScrobbleTest extends BrowserKitTestCase
{
use DatabaseTransactions, WithoutMiddleware;

View file

@ -1,11 +1,15 @@
<?php
namespace Tests\Feature;
use App\Models\Setting;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Media;
use Tests\BrowserKitTestCase;
class SettingTest extends TestCase
class SettingTest extends BrowserKitTestCase
{
use DatabaseTransactions, WithoutMiddleware;
@ -48,8 +52,8 @@ class SettingTest extends TestCase
{
Media::shouldReceive('sync')->once();
$this->actingAs(factory(User::class, 'admin')->create())
->post('/api/settings', ['media_path' => __DIR__])
$user = factory(User::class, 'admin')->create();
$this->postAsUser('/api/settings', ['media_path' => __DIR__], $user)
->seeStatusCode(200);
$this->assertEquals(__DIR__, Setting::get('media_path'));

View file

@ -1,17 +1,21 @@
<?php
namespace Tests\Feature;
use App\Events\LibraryChanged;
use App\Models\Album;
use App\Models\Artist;
use App\Models\Song;
use App\Models\User;
use Aws\AwsClient;
use Cache;
use GuzzleHttp\Psr7\Request;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Mockery as m;
use Tests\BrowserKitTestCase;
class SongTest extends TestCase
class SongTest extends BrowserKitTestCase
{
use DatabaseTransactions, WithoutMiddleware;
@ -26,8 +30,8 @@ class SongTest extends TestCase
$this->expectsEvents(LibraryChanged::class);
$song = Song::orderBy('id', 'desc')->first();
$this->actingAs(factory(User::class, 'admin')->create())
->put('/api/songs', [
$user = factory(User::class, 'admin')->create();
$this->putAsUser('/api/songs', [
'songs' => [$song->id],
'data' => [
'title' => 'Foo Bar',
@ -37,7 +41,7 @@ class SongTest extends TestCase
'track' => 1,
'compilationState' => 0,
],
])
], $user)
->seeStatusCode(200);
$artist = Artist::whereName('John Cena')->first();
@ -59,8 +63,8 @@ class SongTest extends TestCase
$song = Song::orderBy('id', 'desc')->first();
$originalArtistId = $song->album->artist->id;
$this->actingAs(factory(User::class, 'admin')->create())
->put('/api/songs', [
$user = factory(User::class, 'admin')->create();
$this->putAsUser('/api/songs', [
'songs' => [$song->id],
'data' => [
'title' => '',
@ -70,7 +74,7 @@ class SongTest extends TestCase
'track' => 1,
'compilationState' => 0,
],
])
], $user)
->seeStatusCode(200);
// We don't expect the song's artist to change
@ -84,8 +88,8 @@ class SongTest extends TestCase
{
$songIds = Song::orderBy('id', 'desc')->take(3)->pluck('id')->toArray();
$this->actingAs(factory(User::class, 'admin')->create())
->put('/api/songs', [
$user = factory(User::class, 'admin')->create();
$this->putAsUser('/api/songs', [
'songs' => $songIds,
'data' => [
'title' => 'foo',
@ -95,7 +99,7 @@ class SongTest extends TestCase
'track' => 9999,
'compilationState' => 0,
],
])
], $user)
->seeStatusCode(200);
$songs = Song::orderBy('id', 'desc')->take(3)->get();
@ -121,8 +125,8 @@ class SongTest extends TestCase
$originalSongs = Song::orderBy('id', 'desc')->take(3)->get();
$songIds = $originalSongs->pluck('id')->toArray();
$this->actingAs(factory(User::class, 'admin')->create())
->put('/api/songs', [
$user = factory(User::class, 'admin')->create();
$this->putAsUser('/api/songs', [
'songs' => $songIds,
'data' => [
'title' => 'Foo Bar',
@ -132,7 +136,7 @@ class SongTest extends TestCase
'track' => 1,
'compilationState' => 0,
],
])
], $user)
->seeStatusCode(200);
$songs = Song::orderBy('id', 'desc')->take(3)->get();
@ -154,11 +158,10 @@ class SongTest extends TestCase
public function testSingleUpdateAllInfoYesCompilation()
{
$admin = factory(User::class, 'admin')->create();
$song = Song::orderBy('id', 'desc')->first();
$this->actingAs($admin)
->put('/api/songs', [
$user = factory(User::class, 'admin')->create();
$this->putAsUser('/api/songs', [
'songs' => [$song->id],
'data' => [
'title' => 'Foo Bar',
@ -168,7 +171,7 @@ class SongTest extends TestCase
'track' => 1,
'compilationState' => 1,
],
])
], $user)
->seeStatusCode(200);
$compilationAlbum = Album::whereArtistIdAndName(Artist::VARIOUS_ID, 'One by One')->first();
@ -187,8 +190,7 @@ class SongTest extends TestCase
// Now try changing stuff and make sure things work.
// Case 1: Keep compilation state and artist the same
$this->actingAs($admin)
->put('/api/songs', [
$this->putAsUser('/api/songs', [
'songs' => [$song->id],
'data' => [
'title' => 'Barz Qux',
@ -198,7 +200,7 @@ class SongTest extends TestCase
'track' => 1,
'compilationState' => 2,
],
])
], $user)
->seeStatusCode(200);
$compilationAlbum = Album::whereArtistIdAndName(Artist::VARIOUS_ID, 'Two by Two')->first();
@ -214,8 +216,7 @@ class SongTest extends TestCase
]);
// Case 2: Keep compilation state, but change the artist.
$this->actingAs($admin)
->put('/api/songs', [
$this->putAsUser('/api/songs', [
'songs' => [$song->id],
'data' => [
'title' => 'Barz Qux',
@ -225,7 +226,7 @@ class SongTest extends TestCase
'track' => 1,
'compilationState' => 2,
],
])
], $user)
->seeStatusCode(200);
$compilationAlbum = Album::whereArtistIdAndName(Artist::VARIOUS_ID, 'One by One')->first();
@ -241,8 +242,7 @@ class SongTest extends TestCase
]);
// Case 3: Change compilation state only
$this->actingAs($admin)
->put('/api/songs', [
$this->putAsUser('/api/songs', [
'songs' => [$song->id],
'data' => [
'title' => 'Barz Qux',
@ -252,7 +252,7 @@ class SongTest extends TestCase
'track' => 1,
'compilationState' => 0,
],
])
], $user)
->seeStatusCode(200);
$artist = Artist::whereName('Foo Fighters')->first();
@ -267,8 +267,7 @@ class SongTest extends TestCase
// Case 3: Change compilation state and artist
// Remember to set the compliation state back to 1
$this->actingAs($admin)
->put('/api/songs', [
$this->putAsUser('/api/songs', [
'songs' => [$song->id],
'data' => [
'title' => 'Barz Qux',
@ -278,9 +277,8 @@ class SongTest extends TestCase
'track' => 1,
'compilationState' => 1,
],
])
->put('/api/songs', [
], $user)
->putAsUser('/api/songs', [
'songs' => [$song->id],
'data' => [
'title' => 'Twilight of the Thunder God',
@ -290,7 +288,7 @@ class SongTest extends TestCase
'track' => 1,
'compilationState' => 0,
],
])
], $user)
->seeStatusCode(200);
$artist = Artist::whereName('Amon Amarth')->first();

View file

@ -1,9 +1,12 @@
<?php
namespace Tests\Feature;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\BrowserKitTestCase;
class UserTest extends TestCase
class UserTest extends BrowserKitTestCase
{
use DatabaseTransactions;

View file

@ -1,5 +1,7 @@
<?php
namespace Tests\Feature;
use App\Models\Song;
use App\Services\YouTube;
use GuzzleHttp\Client;
@ -7,9 +9,10 @@ use GuzzleHttp\Psr7\Response;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Mockery as m;
use Tests\BrowserKitTestCase;
use YouTube as YouTubeFacade;
class YouTubeTest extends TestCase
class YouTubeTest extends BrowserKitTestCase
{
use DatabaseTransactions, WithoutMiddleware;
@ -18,7 +21,7 @@ class YouTubeTest extends TestCase
$this->withoutEvents();
$client = m::mock(Client::class, [
'get' => new Response(200, [], file_get_contents(__DIR__.'/blobs/youtube/search.json')),
'get' => new Response(200, [], file_get_contents(__DIR__.'../../blobs/youtube/search.json')),
]);
$api = new YouTube(null, $client);
@ -38,6 +41,6 @@ class YouTubeTest extends TestCase
// We test on the facade here
YouTubeFacade::shouldReceive('searchVideosRelatedToSong')->once();
$this->visit("/api/youtube/search/song/{$song->id}");
$this->getAsUser("/api/youtube/search/song/{$song->id}");
}
}

View file

@ -1,19 +1,22 @@
<?php
namespace Tests\Feature;
use App\Services\iTunes;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Response;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Mockery as m;
use Tests\BrowserKitTestCase;
class iTunesTest extends TestCase
class iTunesTest extends BrowserKitTestCase
{
use WithoutMiddleware;
public function testGetTrackUrl()
{
$client = m::mock(Client::class, [
'get' => new Response(200, [], file_get_contents(__DIR__.'/blobs/itunes/track.json')),
'get' => new Response(200, [], file_get_contents(__DIR__.'../../blobs/itunes/track.json')),
]);
$api = new iTunes($client);

View file

@ -1,127 +1,10 @@
<?php
use App\Models\Album;
use App\Models\Artist;
use App\Models\Song;
use App\Models\User;
use Illuminate\Foundation\Testing\TestCase as IlluminateTestCase;
use Illuminate\Support\Facades\Artisan;
namespace Tests;
abstract class TestCase extends IlluminateTestCase
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
{
protected $mediaPath;
protected $coverPath;
public function __construct()
{
parent::__construct();
$this->mediaPath = __DIR__.'/songs';
}
public function setUp()
{
parent::setUp();
$this->prepareForTests();
}
/**
* 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();
$this->coverPath = $app->basePath().'/public/img/covers';
return $app;
}
private function prepareForTests()
{
Artisan::call('migrate');
if (!User::all()->count()) {
Artisan::call('db:seed');
}
if (!file_exists($this->coverPath)) {
mkdir($this->coverPath, 0777, true);
}
}
/**
* Create a sample media set, with a complete artist+album+song trio.
*/
protected function createSampleMediaSet()
{
$artist = factory(Artist::class)->create();
// Sample 3 albums
$albums = factory(Album::class, 3)->create([
'artist_id' => $artist->id,
]);
// 7-15 songs per albums
foreach ($albums as $album) {
factory(Song::class, random_int(7, 15))->create([
'album_id' => $album->id,
]);
}
}
protected function getAsUser($url, $user = null)
{
if (!$user) {
$user = factory(User::class)->create();
}
return $this->get($url, [
'Authorization' => 'Bearer '.JWTAuth::fromUser($user),
]);
}
protected function deleteAsUser($url, $data = [], $user = null)
{
if (!$user) {
$user = factory(User::class)->create();
}
return $this->delete($url, $data, [
'Authorization' => 'Bearer '.JWTAuth::fromUser($user),
]);
}
protected function postAsUser($url, $data, $user = null)
{
if (!$user) {
$user = factory(User::class)->create();
}
return $this->post($url, $data, [
'Authorization' => 'Bearer '.JWTAuth::fromUser($user),
]);
}
protected function putAsUser($url, $data, $user = null)
{
if (!$user) {
$user = factory(User::class)->create();
}
return $this->put($url, $data, [
'Authorization' => 'Bearer '.JWTAuth::fromUser($user),
]);
}
use CreatesApplication;
}

View file

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

View file

@ -1,4 +1,4 @@
{
"foo.css": "foo00.css",
"bar.js": "bar00.js"
"/foo.css": "/foo00.css",
"/bar.js": "/bar00.js"
}

393
webpack.config.js Normal file
View file

@ -0,0 +1,393 @@
let path = require('path');
let webpack = require('webpack');
let Mix = require('laravel-mix').config;
let plugins = require('laravel-mix').plugins;
/*
|--------------------------------------------------------------------------
| Mix Initialization
|--------------------------------------------------------------------------
|
| As our first step, we'll require the project's Laravel Mix file
| and record the user's requested compilation and build steps.
| Once those steps have been recorded, we may get to work.
|
*/
Mix.initialize();
/*
|--------------------------------------------------------------------------
| Webpack Context
|--------------------------------------------------------------------------
|
| This prop will determine the appropriate context, when running Webpack.
| Since you have the option of publishing this webpack.config.js file
| to your project root, we will dynamically set the path for you.
|
*/
module.exports.context = Mix.Paths.root();
/*
|--------------------------------------------------------------------------
| Webpack Entry
|--------------------------------------------------------------------------
|
| We'll first specify the entry point for Webpack. By default, we'll
| assume a single bundled file, but you may call Mix.extract()
| to make a separate bundle specifically for vendor libraries.
|
*/
module.exports.entry = Mix.entry();
/*
|--------------------------------------------------------------------------
| Webpack Output
|--------------------------------------------------------------------------
|
| Webpack naturally requires us to specify our desired output path and
| file name. We'll simply echo what you passed to with Mix.js().
| Note that, for Mix.version(), we'll properly hash the file.
|
*/
module.exports.output = Mix.output();
/*
|--------------------------------------------------------------------------
| Rules
|--------------------------------------------------------------------------
|
| Webpack rules allow us to register any number of loaders and options.
| Out of the box, we'll provide a handful to get you up and running
| as quickly as possible, though feel free to add to this list.
|
*/
module.exports.module = {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: Mix.options.extractVueStyles ? {
js: 'babel-loader' + Mix.babelConfig(),
scss: plugins.ExtractTextPlugin.extract({
use: 'css-loader!sass-loader',
fallback: 'vue-style-loader'
}),
sass: plugins.ExtractTextPlugin.extract({
use: 'css-loader!sass-loader?indentedSyntax',
fallback: 'vue-style-loader'
}),
css: plugins.ExtractTextPlugin.extract({
use: 'css-loader',
fallback: 'vue-style-loader'
})
}: {
js: 'babel-loader' + Mix.babelConfig(),
scss: 'vue-style-loader!css-loader!sass-loader',
sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax'
},
postcss: [
require('autoprefixer')
]
}
},
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader' + Mix.babelConfig()
},
{
test: /\.css$/,
loaders: ['style-loader', 'css-loader']
},
{
test: /\.html$/,
loaders: ['html-loader']
},
{
test: /\.(png|jpg|gif)$/,
loader: 'file-loader',
options: {
name: 'images/[name].[ext]?[hash]',
publicPath: '/public/'
}
},
{
test: /\.(woff2?|ttf|eot|svg|otf)$/,
loader: 'file-loader',
options: {
name: 'fonts/[name].[ext]?[hash]',
publicPath: '/public/'
}
}
]
};
if (Mix.preprocessors) {
Mix.preprocessors.forEach(toCompile => {
let extractPlugin = new plugins.ExtractTextPlugin(
Mix.cssOutput(toCompile)
);
let sourceMap = Mix.sourcemaps ? '?sourceMap' : '';
module.exports.module.rules.push({
test: new RegExp(toCompile.src.path.replace(/\\/g, '\\\\') + '$'),
use: extractPlugin.extract({
fallback: 'style-loader',
use: [
{ loader: 'css-loader' + sourceMap },
{ loader: 'postcss-loader' + sourceMap }
].concat(
toCompile.type == 'sass' ? [
{ loader: 'resolve-url-loader' + sourceMap },
{
loader: 'sass-loader',
options: Object.assign({
precision: 8,
outputStyle: 'expanded'
}, toCompile.pluginOptions, { sourceMap: true })
}
] : [
{
loader: 'less-loader' + sourceMap,
options: toCompile.pluginOptions
}
]
)
})
});
module.exports.plugins = (module.exports.plugins || []).concat(extractPlugin);
});
} else if (Mix.options.extractVueStyles) {
module.exports.plugins = (module.exports.plugins || []).concat(
new plugins.ExtractTextPlugin(path.join(Mix.js.base, 'vue-styles.css'))
);
}
/*
|--------------------------------------------------------------------------
| Resolve
|--------------------------------------------------------------------------
|
| Here, we may set any options/aliases that affect Webpack's resolving
| of modules. To begin, we will provide the necessary Vue alias to
| load the Vue common library. You may delete this, if needed.
|
*/
module.exports.resolve = {
extensions: ['*', '.js', '.jsx', '.vue'],
alias: {
'vue$': 'vue/dist/vue.common.js'
}
};
/*
|--------------------------------------------------------------------------
| Stats
|--------------------------------------------------------------------------
|
| By default, Webpack spits a lot of information out to the terminal,
| each you time you compile. Let's keep things a bit more minimal
| and hide a few of those bits and pieces. Adjust as you wish.
|
*/
module.exports.stats = {
hash: false,
version: false,
timings: false,
children: false,
errors: false
};
module.exports.performance = { hints: false };
/*
|--------------------------------------------------------------------------
| Devtool
|--------------------------------------------------------------------------
|
| Sourcemaps allow us to access our original source code within the
| browser, even if we're serving a bundled script or stylesheet.
| You may activate sourcemaps, by adding Mix.sourceMaps().
|
*/
module.exports.devtool = Mix.sourcemaps;
/*
|--------------------------------------------------------------------------
| Webpack Dev Server Configuration
|--------------------------------------------------------------------------
|
| If you want to use that flashy hot module replacement feature, then
| we've got you covered. Here, we'll set some basic initial config
| for the Node server. You very likely won't want to edit this.
|
*/
module.exports.devServer = {
historyApiFallback: true,
noInfo: true,
compress: true,
quiet: true
};
/*
|--------------------------------------------------------------------------
| Plugins
|--------------------------------------------------------------------------
|
| Lastly, we'll register a number of plugins to extend and configure
| Webpack. To get you started, we've included a handful of useful
| extensions, for versioning, OS notifications, and much more.
|
*/
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.ProvidePlugin(Mix.autoload || {
jQuery: 'jquery',
$: 'jquery',
jquery: 'jquery',
'window.jQuery': 'jquery'
}),
new plugins.FriendlyErrorsWebpackPlugin(),
new plugins.StatsWriterPlugin({
filename: 'mix-manifest.json',
transform: Mix.manifest.transform.bind(Mix.manifest),
}),
new plugins.WebpackMd5HashPlugin(),
new webpack.LoaderOptionsPlugin({
minimize: Mix.inProduction,
options: {
postcss: [
require('autoprefixer')
],
context: __dirname,
output: { path: './' }
}
})
]);
if (Mix.browserSync) {
module.exports.plugins.push(
new plugins.BrowserSyncPlugin(Object.assign({
host: 'localhost',
port: 3000,
proxy: 'app.dev',
files: [
'app/**/*.php',
'resources/views/**/*.php',
'public/mix-manifest.json',
'public/css/**/*.css',
'public/js/**/*.js'
]
}, Mix.browserSync))
);
}
module.exports.plugins.push(
new plugins.WebpackOnBuildPlugin(
stats => Mix.events.fire('build', stats)
)
);
if (Mix.notifications) {
module.exports.plugins.push(
new plugins.WebpackNotifierPlugin({
title: 'Laravel Mix',
alwaysNotify: true,
contentImage: Mix.Paths.root('node_modules/laravel-mix/icons/laravel.png')
})
);
}
if (Mix.copy) {
Mix.copy.forEach(copy => {
module.exports.plugins.push(
new plugins.CopyWebpackPlugin([copy])
);
});
}
if (Mix.extract) {
module.exports.plugins.push(
new webpack.optimize.CommonsChunkPlugin({
names: Mix.entryBuilder.extractions.concat([
path.join(Mix.js.base, 'manifest').replace(/\\/g, '/')
]),
minChunks: Infinity
})
);
}
if (Mix.inProduction) {
module.exports.plugins = module.exports.plugins.concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
warnings: false,
drop_console: true
}
})
]);
}
/*
|--------------------------------------------------------------------------
| Mix Finalizing
|--------------------------------------------------------------------------
|
| Now that we've declared the entirety of our Webpack configuration, the
| final step is to scan for any custom configuration in the Mix file.
| If mix.webpackConfig() is called, we'll merge it in, and build!
|
*/
Mix.finalize(module.exports);

28
webpack.mix.js Normal file
View file

@ -0,0 +1,28 @@
const mix = require('laravel-mix');
const fs = require('fs');
mix.config.detectHotReloading()
if (mix.config.hmr) {
// There's a bug with Mix/copy plugin which prevents HMR from working:
// https://github.com/JeffreyWay/laravel-mix/issues/150
console.log('In HMR mode. If assets are missing, Ctr+C and run `yarn dev` first.');
// Somehow public/hot isn't being removed by Mix. We'll handle it ourselves.
process.on('SIGINT', () => {
try {
fs.unlinkSync(mix.config.publicPath + '/hot');
} catch (e) {}
process.exit();
});
} else {
mix.copy('resources/assets/img', 'public/img')
.copy('node_modules/font-awesome/fonts', 'public/fonts');
}
mix.js('resources/assets/js/app.js', 'public/js')
.sass('resources/assets/sass/app.scss', 'public/css');
if (mix.config.inProduction) {
mix.version();
mix.disableNotifications();
}

4363
yarn.lock

File diff suppressed because it is too large Load diff