mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
commit
725f8f7747
@ -1 +1 @@
|
||||
5.5.61
|
||||
5.5.62
|
@ -54,6 +54,7 @@ use Database\Factories\BankTransactionRuleFactory;
|
||||
use Faker\Factory;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Str;
|
||||
use stdClass;
|
||||
@ -203,6 +204,22 @@ class CreateSingleAccount extends Command
|
||||
'applies_to' => (bool)rand(0,1) ? 'CREDIT' : 'DEBIT',
|
||||
]);
|
||||
|
||||
$client = Client::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
'name' => 'cypress'
|
||||
]);
|
||||
|
||||
ClientContact::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'client_id' => $client->id,
|
||||
'company_id' => $company->id,
|
||||
'is_primary' => 1,
|
||||
'email' => 'cypress@example.com',
|
||||
'password' => Hash::make('password'),
|
||||
]);
|
||||
|
||||
|
||||
$this->info('Creating '.$this->count.' clients');
|
||||
|
||||
for ($x = 0; $x < $this->count; $x++) {
|
||||
@ -356,7 +373,7 @@ class CreateSingleAccount extends Command
|
||||
'client_id' => $client->id,
|
||||
'company_id' => $company->id,
|
||||
'is_primary' => 1,
|
||||
'email' => 'user@example.com'
|
||||
'email' => 'user@example.com',
|
||||
]);
|
||||
|
||||
ClientContact::factory()->count(rand(1, 2))->create([
|
||||
|
@ -149,6 +149,7 @@ class NinjaPlanController extends Controller
|
||||
$account->plan_started = now();
|
||||
$account->plan_expires = now()->addDays(14);
|
||||
$account->is_trial=true;
|
||||
$account->hosted_company_count = 10;
|
||||
$account->save();
|
||||
}
|
||||
|
||||
|
@ -148,21 +148,21 @@ class AdjustProductInventory implements ShouldQueue
|
||||
$nmo->company = $this->company;
|
||||
$nmo->settings = $this->company->settings;
|
||||
|
||||
// $product->company_users->each(function ($cu) use($product, $nmo){
|
||||
$this->company->company_users->each(function ($cu) use($product, $nmo){
|
||||
|
||||
// if($this->checkNotificationExists($cu, $product, ['inventory_all', 'inventory_user']))
|
||||
// {
|
||||
if($this->checkNotificationExists($cu, $product, ['inventory_all', 'inventory_user']))
|
||||
{
|
||||
|
||||
// $nmo->to_user = $cu->user;
|
||||
// NinjaMailerJob::dispatch($nmo);
|
||||
$nmo->to_user = $cu->user;
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
// });
|
||||
});
|
||||
|
||||
$nmo->to_user = $this->company->owner();
|
||||
// $nmo->to_user = $this->company->owner();
|
||||
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
// NinjaMailerJob::dispatch($nmo);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ class QuoteCheckExpired implements ShouldQueue
|
||||
}
|
||||
|
||||
/* Returns an array of notification methods */
|
||||
$methods = $this->findUserNotificationTypes($quote->invitations()->first(), $company_user, 'quote', ['all_notifications', 'quote_expired', 'quote_expired_all']);
|
||||
$methods = $this->findUserNotificationTypes($quote->invitations()->first(), $company_user, 'quote', ['all_notifications', 'quote_expired', 'quote_expired_all', 'quote_expired_user']);
|
||||
|
||||
/* If one of the methods is email then we fire the EntitySentMailer */
|
||||
if (($key = array_search('mail', $methods)) !== false) {
|
||||
|
@ -57,7 +57,7 @@ class CreditCreatedNotification implements ShouldQueue
|
||||
// $notification = new EntitySentNotification($event->invitation, 'credit');
|
||||
|
||||
/* Returns an array of notification methods */
|
||||
$methods = $this->findUserNotificationTypes($credit->invitations()->first(), $company_user, 'credit', ['all_notifications', 'credit_created', 'credit_created_all']);
|
||||
$methods = $this->findUserNotificationTypes($credit->invitations()->first(), $company_user, 'credit', ['all_notifications', 'credit_created', 'credit_created_all', 'credit_created_user']);
|
||||
|
||||
/* If one of the methods is email then we fire the EntitySentMailer */
|
||||
if (($key = array_search('mail', $methods)) !== false) {
|
||||
|
@ -54,7 +54,7 @@ class CreditEmailedNotification implements ShouldQueue
|
||||
|
||||
// $notification = new EntitySentNotification($event->invitation, 'credit');
|
||||
|
||||
$methods = $this->findUserNotificationTypes($event->invitation, $company_user, 'credit', ['all_notifications', 'credit_sent', 'credit_sent_all']);
|
||||
$methods = $this->findUserNotificationTypes($event->invitation, $company_user, 'credit', ['all_notifications', 'credit_sent', 'credit_sent_all', 'credit_sent_user']);
|
||||
|
||||
if (($key = array_search('mail', $methods)) !== false) {
|
||||
// if (($key = array_search('mail', $methods))) {
|
||||
|
@ -63,7 +63,7 @@ class InvoiceCreatedNotification implements ShouldQueue
|
||||
// $notification = new EntitySentNotification($event->invitation, 'invoice');
|
||||
|
||||
/* Returns an array of notification methods */
|
||||
$methods = $this->findUserNotificationTypes($invoice->invitations()->first(), $company_user, 'invoice', ['all_notifications', 'invoice_created', 'invoice_created_all']);
|
||||
$methods = $this->findUserNotificationTypes($invoice->invitations()->first(), $company_user, 'invoice', ['all_notifications', 'invoice_created', 'invoice_created_all', 'invoice_created_user']);
|
||||
/* If one of the methods is email then we fire the EntitySentMailer */
|
||||
|
||||
if (($key = array_search('mail', $methods)) !== false) {
|
||||
|
@ -61,7 +61,7 @@ class InvoiceEmailedNotification implements ShouldQueue
|
||||
// $notification = new EntitySentNotification($event->invitation, 'invoice');
|
||||
|
||||
/* Returns an array of notification methods */
|
||||
$methods = $this->findUserNotificationTypes($event->invitation, $company_user, 'invoice', ['all_notifications', 'invoice_sent', 'invoice_sent_all']);
|
||||
$methods = $this->findUserNotificationTypes($event->invitation, $company_user, 'invoice', ['all_notifications', 'invoice_sent', 'invoice_sent_all', 'invoice_sent_user']);
|
||||
|
||||
/* If one of the methods is email then we fire the EntitySentMailer */
|
||||
if (($key = array_search('mail', $methods)) !== false) {
|
||||
|
@ -56,7 +56,7 @@ class InvoiceFailedEmailNotification
|
||||
foreach ($event->invitation->company->company_users as $company_user) {
|
||||
$user = $company_user->user;
|
||||
|
||||
$methods = $this->findUserNotificationTypes($event->invitation, $company_user, 'invoice', ['all_notifications', 'invoice_sent', 'invoice_sent_all']);
|
||||
$methods = $this->findUserNotificationTypes($event->invitation, $company_user, 'invoice', ['all_notifications', 'invoice_sent', 'invoice_sent_all', 'invoice_sent_user']);
|
||||
|
||||
if (($key = array_search('mail', $methods)) !== false) {
|
||||
unset($methods[$key]);
|
||||
|
@ -68,8 +68,9 @@ class InvitationViewedListener implements ShouldQueue
|
||||
foreach ($invitation->company->company_users as $company_user) {
|
||||
$entity_viewed = "{$entity_name}_viewed";
|
||||
$entity_viewed_all = "{$entity_name}_viewed_all";
|
||||
$entity_viewed_user = "{$entity_name}_viewed_user";
|
||||
|
||||
$methods = $this->findUserNotificationTypes($invitation, $company_user, $entity_name, ['all_notifications', $entity_viewed, $entity_viewed_all]);
|
||||
$methods = $this->findUserNotificationTypes($invitation, $company_user, $entity_name, ['all_notifications', $entity_viewed, $entity_viewed_all, $entity_viewed_user]);
|
||||
|
||||
if (($key = array_search('mail', $methods)) !== false) {
|
||||
unset($methods[$key]);
|
||||
|
@ -59,7 +59,7 @@ class PurchaseOrderAcceptedListener implements ShouldQueue
|
||||
}
|
||||
|
||||
/* Returns an array of notification methods */
|
||||
$methods = $this->findUserNotificationTypes($purchase_order->invitations()->first(), $company_user, 'purchase_order', ['all_notifications', 'purchase_order_accepted', 'purchase_order_accepted_all']);
|
||||
$methods = $this->findUserNotificationTypes($purchase_order->invitations()->first(), $company_user, 'purchase_order', ['all_notifications', 'purchase_order_accepted', 'purchase_order_accepted_all', 'purchase_order_accepted_user']);
|
||||
|
||||
/* If one of the methods is email then we fire the EntitySentMailer */
|
||||
if (($key = array_search('mail', $methods)) !== false) {
|
||||
|
@ -63,7 +63,7 @@ class PurchaseOrderCreatedListener implements ShouldQueue
|
||||
// $notification = new EntitySentNotification($event->invitation, 'purchase_order');
|
||||
|
||||
/* Returns an array of notification methods */
|
||||
$methods = $this->findUserNotificationTypes($purchase_order->invitations()->first(), $company_user, 'purchase_order', ['all_notifications', 'purchase_order_created', 'purchase_order_created_all']);
|
||||
$methods = $this->findUserNotificationTypes($purchase_order->invitations()->first(), $company_user, 'purchase_order', ['all_notifications', 'purchase_order_created', 'purchase_order_created_all', 'purchase_order_created_user']);
|
||||
/* If one of the methods is email then we fire the EntitySentMailer */
|
||||
|
||||
if (($key = array_search('mail', $methods)) !== false) {
|
||||
|
@ -61,7 +61,7 @@ class PurchaseOrderEmailedNotification implements ShouldQueue
|
||||
// $notification = new EntitySentNotification($event->invitation, 'purchase_order');
|
||||
|
||||
/* Returns an array of notification methods */
|
||||
$methods = $this->findUserNotificationTypes($event->invitation, $company_user, 'purchase_order', ['all_notifications', 'purchase_order_sent', 'purchase_order_sent_all']);
|
||||
$methods = $this->findUserNotificationTypes($event->invitation, $company_user, 'purchase_order', ['all_notifications', 'purchase_order_sent', 'purchase_order_sent_all', 'purchase_order_sent_user']);
|
||||
|
||||
/* If one of the methods is email then we fire the EntitySentMailer */
|
||||
if (($key = array_search('mail', $methods)) !== false) {
|
||||
|
@ -61,7 +61,7 @@ class QuoteApprovedNotification implements ShouldQueue
|
||||
}
|
||||
|
||||
/* Returns an array of notification methods */
|
||||
$methods = $this->findUserNotificationTypes($quote->invitations()->first(), $company_user, 'quote', ['all_notifications', 'quote_approved', 'quote_approved_all']);
|
||||
$methods = $this->findUserNotificationTypes($quote->invitations()->first(), $company_user, 'quote', ['all_notifications', 'quote_approved', 'quote_approved_all', 'quote_approved_user']);
|
||||
|
||||
/* If one of the methods is email then we fire the EntitySentMailer */
|
||||
if (($key = array_search('mail', $methods)) !== false) {
|
||||
|
@ -63,7 +63,7 @@ class QuoteCreatedNotification implements ShouldQueue
|
||||
// $notification = new EntitySentNotification($event->invitation, 'quote');
|
||||
|
||||
/* Returns an array of notification methods */
|
||||
$methods = $this->findUserNotificationTypes($quote->invitations()->first(), $company_user, 'quote', ['all_notifications', 'quote_created', 'quote_created_all']);
|
||||
$methods = $this->findUserNotificationTypes($quote->invitations()->first(), $company_user, 'quote', ['all_notifications', 'quote_created', 'quote_created_all', 'quote_created_user']);
|
||||
|
||||
/* If one of the methods is email then we fire the EntitySentMailer */
|
||||
if (($key = array_search('mail', $methods)) !== false) {
|
||||
|
@ -56,7 +56,7 @@ class QuoteEmailedNotification implements ShouldQueue
|
||||
|
||||
// $notification = new EntitySentNotification($event->invitation, 'quote');
|
||||
|
||||
$methods = $this->findUserNotificationTypes($event->invitation, $company_user, 'quote', ['all_notifications', 'quote_sent', 'quote_sent_all']);
|
||||
$methods = $this->findUserNotificationTypes($event->invitation, $company_user, 'quote', ['all_notifications', 'quote_sent', 'quote_sent_all', 'quote_sent_user']);
|
||||
|
||||
if (($key = array_search('mail', $methods)) !== false) {
|
||||
unset($methods[$key]);
|
||||
|
@ -27,7 +27,6 @@ class BankIntegration extends BaseModel
|
||||
'bank_account_type',
|
||||
'balance',
|
||||
'currency',
|
||||
'nickname',
|
||||
'from_date',
|
||||
'auto_sync',
|
||||
];
|
||||
|
@ -358,7 +358,7 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
public function hasPermission($permission) : bool
|
||||
{
|
||||
$parts = explode('_', $permission);
|
||||
$all_permission = false;
|
||||
$all_permission = '____';
|
||||
|
||||
if (count($parts) > 1) {
|
||||
$all_permission = $parts[0].'_all';
|
||||
@ -366,8 +366,8 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
|
||||
return $this->isOwner() ||
|
||||
$this->isAdmin() ||
|
||||
(stripos($all_permission, $this->token()->cu->permissions) !== false) ||
|
||||
(stripos($permission, $this->token()->cu->permissions) !== false);
|
||||
(stripos($this->token()->cu->permissions, $all_permission) !== false) ||
|
||||
(stripos($this->token()->cu->permissions, $permission) !== false);
|
||||
|
||||
// return $this->isOwner() ||
|
||||
// $this->isAdmin() ||
|
||||
|
@ -111,6 +111,7 @@ class CreditCard
|
||||
'value' => $amount,
|
||||
],
|
||||
'description' => $description,
|
||||
'idempotencyKey' => uniqid("st",true),
|
||||
'redirectUrl' => route('mollie.3ds_redirect', [
|
||||
'company_key' => $this->mollie->client->company->company_key,
|
||||
'company_gateway_id' => $this->mollie->company_gateway->hashed_id,
|
||||
|
@ -102,7 +102,10 @@ class ClientRepository extends BaseRepository
|
||||
$data['name'] = $client->present()->name();
|
||||
}
|
||||
|
||||
$this->contact_repo->save($contact_data, $client);
|
||||
//24-01-2023 when a logo is uploaded, no other data is set, so we need to catch here and not update
|
||||
//the contacts array UNLESS there are no contacts and we need to maintain state.
|
||||
if(array_key_exists('contacts', $contact_data) || $client->contacts()->count() == 0)
|
||||
$this->contact_repo->save($contact_data, $client);
|
||||
|
||||
return $client;
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ trait UserNotifies
|
||||
|
||||
//if a user owns this record or is assigned to it, they are attached the permission for notification.
|
||||
if ($invitation->{$entity_name}->user_id == $company_user->user_id || $invitation->{$entity_name}->assigned_user_id == $company_user->user_id) {
|
||||
$required_permissions = $this->addSpecialUserPermissionForEntity($invitation->{$entity_name}, $required_permissions);
|
||||
// $required_permissions = $this->addSpecialUserPermissionForEntity($invitation->{$entity_name}, $required_permissions);
|
||||
} else {
|
||||
$required_permissions = $this->removeSpecialUserPermissionForEntity($invitation->{$entity_name}, $required_permissions);
|
||||
}
|
||||
@ -83,24 +83,24 @@ trait UserNotifies
|
||||
|
||||
private function addSpecialUserPermissionForEntity($entity, array $required_permissions) :array
|
||||
{
|
||||
array_merge($required_permissions, ['all_notifications']);
|
||||
return array_merge($required_permissions, ['all_notifications', 'all_user_notifications']);
|
||||
|
||||
switch ($entity) {
|
||||
case $entity instanceof Payment || $entity instanceof Client: //we pass client also as this is the proxy for Payment Failures (ie, there is no payment)
|
||||
return array_merge($required_permissions, ['all_notifications', 'all_user_notifications', 'payment_failure_user', 'payment_success_user']);
|
||||
case $entity instanceof Invoice:
|
||||
return array_merge($required_permissions, ['all_notifications', 'all_user_notifications', 'invoice_created_user', 'invoice_sent_user', 'invoice_viewed_user', 'invoice_late_user']);
|
||||
case $entity instanceof Quote:
|
||||
return array_merge($required_permissions, ['all_notifications', 'all_user_notifications', 'quote_created_user', 'quote_sent_user', 'quote_viewed_user', 'quote_approved_user', 'quote_expired_user']);
|
||||
case $entity instanceof Credit:
|
||||
return array_merge($required_permissions, ['all_notifications', 'all_user_notifications', 'credit_created_user', 'credit_sent_user', 'credit_viewed_user']);
|
||||
case $entity instanceof PurchaseOrder:
|
||||
return array_merge($required_permissions, ['all_notifications', 'all_user_notifications', 'purchase_order_created_user', 'purchase_order_sent_user', 'purchase_order_viewed_user']);
|
||||
case $entity instanceof Product:
|
||||
return array_merge($required_permissions, ['all_notifications', 'all_user_notifications', 'inventory_user', 'inventory_all']);
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
// switch ($entity) {
|
||||
// case $entity instanceof Payment || $entity instanceof Client: //we pass client also as this is the proxy for Payment Failures (ie, there is no payment)
|
||||
// return array_merge($required_permissions, ['all_notifications', 'all_user_notifications', 'payment_failure_user', 'payment_success_user']);
|
||||
// case $entity instanceof Invoice:
|
||||
// return array_merge($required_permissions, ['all_notifications', 'all_user_notifications', 'invoice_created_user', 'invoice_sent_user', 'invoice_viewed_user', 'invoice_late_user']);
|
||||
// case $entity instanceof Quote:
|
||||
// return array_merge($required_permissions, ['all_notifications', 'all_user_notifications', 'quote_created_user', 'quote_sent_user', 'quote_viewed_user', 'quote_approved_user', 'quote_expired_user']);
|
||||
// case $entity instanceof Credit:
|
||||
// return array_merge($required_permissions, ['all_notifications', 'all_user_notifications', 'credit_created_user', 'credit_sent_user', 'credit_viewed_user']);
|
||||
// case $entity instanceof PurchaseOrder:
|
||||
// return array_merge($required_permissions, ['all_notifications', 'all_user_notifications', 'purchase_order_created_user', 'purchase_order_sent_user', 'purchase_order_viewed_user']);
|
||||
// case $entity instanceof Product:
|
||||
// return array_merge($required_permissions, ['all_notifications', 'all_user_notifications', 'inventory_user', 'inventory_all']);
|
||||
// default:
|
||||
// return [];
|
||||
// }
|
||||
}
|
||||
|
||||
private function removeSpecialUserPermissionForEntity($entity, $required_permissions)
|
||||
|
@ -101,6 +101,7 @@
|
||||
"darkaonline/l5-swagger": "8.1.0",
|
||||
"fakerphp/faker": "^1.14",
|
||||
"filp/whoops": "^2.7",
|
||||
"laracasts/cypress": "^3.0",
|
||||
"laravel/dusk": "^6.15",
|
||||
"mockery/mockery": "^1.4.4",
|
||||
"nunomaduro/collision": "^6.1",
|
||||
|
61
composer.lock
generated
61
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "451d3dbdd5b0a87940e0f8fffadab4ae",
|
||||
"content-hash": "fee0057b8444e2a245cea87dab6c1b3a",
|
||||
"packages": [
|
||||
{
|
||||
"name": "afosto/yaac",
|
||||
@ -13859,6 +13859,65 @@
|
||||
},
|
||||
"time": "2020-07-09T08:09:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laracasts/cypress",
|
||||
"version": "3.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laracasts/cypress.git",
|
||||
"reference": "9a9e5d25a51d2cbb410393e6a0d9883aa3304bf5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laracasts/cypress/zipball/9a9e5d25a51d2cbb410393e6a0d9883aa3304bf5",
|
||||
"reference": "9a9e5d25a51d2cbb410393e6a0d9883aa3304bf5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/support": "^6.0|^7.0|^8.0|^9.0",
|
||||
"php": "^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"orchestra/testbench": "^6.0|^7.0",
|
||||
"phpunit/phpunit": "^8.0|^9.5.10",
|
||||
"spatie/laravel-ray": "^1.29"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Laracasts\\Cypress\\CypressServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Laracasts\\Cypress\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jeffrey Way",
|
||||
"email": "jeffrey@laracasts.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Laravel Cypress Boilerplate",
|
||||
"homepage": "https://github.com/laracasts/cypress",
|
||||
"keywords": [
|
||||
"cypress",
|
||||
"laracasts"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/laracasts/cypress/issues",
|
||||
"source": "https://github.com/laracasts/cypress/tree/3.0.0"
|
||||
},
|
||||
"time": "2022-06-27T13:49:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/dusk",
|
||||
"version": "v6.25.2",
|
||||
|
@ -14,8 +14,8 @@ return [
|
||||
'require_https' => env('REQUIRE_HTTPS', true),
|
||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||
'app_version' => '5.5.61',
|
||||
'app_tag' => '5.5.61',
|
||||
'app_version' => '5.5.62',
|
||||
'app_tag' => '5.5.62',
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', ''),
|
||||
|
19
cypress.config.js
vendored
Normal file
19
cypress.config.js
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
const { defineConfig } = require('cypress')
|
||||
|
||||
module.exports = defineConfig({
|
||||
chromeWebSecurity: false,
|
||||
retries: 2,
|
||||
defaultCommandTimeout: 5000,
|
||||
watchForFileChanges: false,
|
||||
videosFolder: 'tests/cypress/videos',
|
||||
screenshotsFolder: 'tests/cypress/screenshots',
|
||||
fixturesFolder: 'tests/cypress/fixture',
|
||||
e2e: {
|
||||
setupNodeEvents(on, config) {
|
||||
return require('./tests/cypress/plugins/index.js')(on, config)
|
||||
},
|
||||
baseUrl: 'http://ninja.test:8000/',
|
||||
specPattern: 'tests/cypress/integration/**/*.cy.{js,jsx,ts,tsx}',
|
||||
supportFile: 'tests/cypress/support/index.js',
|
||||
},
|
||||
})
|
@ -118,67 +118,23 @@ class RandomDataSeeder extends Seeder
|
||||
'settings' => null,
|
||||
]);
|
||||
|
||||
$u2 = User::where('email', 'demo@invoiceninja.com')->first();
|
||||
|
||||
if (! $u2) {
|
||||
$u2 = User::factory()->create([
|
||||
'email' => 'demo@invoiceninja.com',
|
||||
'password' => Hash::make('demo'),
|
||||
'account_id' => $account->id,
|
||||
'confirmation_code' => $this->createDbHash(config('database.default')),
|
||||
]);
|
||||
|
||||
$company_token = CompanyToken::create([
|
||||
'user_id' => $u2->id,
|
||||
'company_id' => $company->id,
|
||||
'account_id' => $account->id,
|
||||
'name' => 'test token',
|
||||
'token' => 'TOKEN',
|
||||
]);
|
||||
|
||||
$u2->companies()->attach($company->id, [
|
||||
'account_id' => $account->id,
|
||||
'is_owner' => 1,
|
||||
'is_admin' => 1,
|
||||
'is_locked' => 0,
|
||||
'notifications' => CompanySettings::notificationDefaults(),
|
||||
'permissions' => '',
|
||||
'settings' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
$client = Client::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
]);
|
||||
|
||||
ClientContact::create([
|
||||
'first_name' => $faker->firstName(),
|
||||
'last_name' => $faker->lastName(),
|
||||
'email' => config('ninja.testvars.username'),
|
||||
'company_id' => $company->id,
|
||||
'password' => Hash::make(config('ninja.testvars.password')),
|
||||
'email_verified_at' => now(),
|
||||
'client_id' =>$client->id,
|
||||
'user_id' => $user->id,
|
||||
'is_primary' => true,
|
||||
'contact_key' => \Illuminate\Support\Str::random(40),
|
||||
]);
|
||||
|
||||
Client::factory()->create(['user_id' => $user->id, 'company_id' => $company->id])->each(function ($c) use ($user, $company) {
|
||||
ClientContact::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'client_id' => $c->id,
|
||||
'company_id' => $company->id,
|
||||
'is_primary' => 1,
|
||||
'name' => 'cypress'
|
||||
]);
|
||||
|
||||
ClientContact::factory()->count(5)->create([
|
||||
'user_id' => $user->id,
|
||||
'client_id' => $c->id,
|
||||
'company_id' => $company->id,
|
||||
]);
|
||||
});
|
||||
$client->number = $client->getNextClientNumber($client);
|
||||
$client->save();
|
||||
|
||||
ClientContact::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'client_id' => $client->id,
|
||||
'company_id' => $company->id,
|
||||
'is_primary' => 1,
|
||||
'email' => 'cypress@example.com',
|
||||
'password' => Hash::make('password'),
|
||||
]);
|
||||
|
||||
/* Product Factory */
|
||||
Product::factory()->count(2)->create(['user_id' => $user->id, 'company_id' => $company->id]);
|
||||
@ -200,8 +156,6 @@ class RandomDataSeeder extends Seeder
|
||||
|
||||
$invoice = $invoice_calc->build()->getInvoice();
|
||||
|
||||
$invoice->save();
|
||||
|
||||
$invoice->service()->createInvitations()->markSent()->save();
|
||||
|
||||
$invoice->ledger()->updateInvoiceBalance($invoice->balance);
|
||||
@ -220,16 +174,16 @@ class RandomDataSeeder extends Seeder
|
||||
|
||||
$payment->invoices()->save($invoice);
|
||||
|
||||
$payment_hash = new PaymentHash;
|
||||
$payment_hash->hash = Str::random(128);
|
||||
$payment_hash->data = [['invoice_id' => $invoice->hashed_id, 'amount' => $invoice->balance]];
|
||||
$payment_hash->fee_total = 0;
|
||||
$payment_hash->fee_invoice_id = $invoice->id;
|
||||
$payment_hash->save();
|
||||
// $payment_hash = new PaymentHash;
|
||||
// $payment_hash->hash = Str::random(128);
|
||||
// $payment_hash->data = [['invoice_id' => $invoice->hashed_id, 'amount' => $invoice->balance]];
|
||||
// $payment_hash->fee_total = 0;
|
||||
// $payment_hash->fee_invoice_id = $invoice->id;
|
||||
// $payment_hash->save();
|
||||
|
||||
event(new PaymentWasCreated($payment, $payment->company, Ninja::eventVars()));
|
||||
|
||||
$payment->service()->updateInvoicePayment($payment_hash);
|
||||
// $payment->service()->updateInvoicePayment($payment_hash);
|
||||
|
||||
// UpdateInvoicePayment::dispatchNow($payment, $payment->company);
|
||||
}
|
||||
@ -256,7 +210,6 @@ class RandomDataSeeder extends Seeder
|
||||
|
||||
$credit->service()->createInvitations()->markSent()->save();
|
||||
|
||||
//$invoice->markSent()->save();
|
||||
});
|
||||
|
||||
/* Recurring Invoice Factory */
|
||||
@ -286,14 +239,6 @@ class RandomDataSeeder extends Seeder
|
||||
//$invoice->markSent()->save();
|
||||
});
|
||||
|
||||
$clients = Client::all();
|
||||
|
||||
foreach ($clients as $client) {
|
||||
//$client->getNextClientNumber($client);
|
||||
$client->number = $client->getNextClientNumber($client);
|
||||
$client->save();
|
||||
}
|
||||
|
||||
GroupSetting::create([
|
||||
'company_id' => $company->id,
|
||||
'user_id' => $user->id,
|
||||
|
2478
package-lock.json
generated
2478
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -12,12 +12,13 @@
|
||||
"@babel/compat-data": "7.15.0",
|
||||
"@babel/plugin-proposal-class-properties": "^7.14.5",
|
||||
"@tailwindcss/aspect-ratio": "^0.4.2",
|
||||
"cypress": "^12.3.0",
|
||||
"laravel-mix-purgecss": "^6.0.0",
|
||||
"vue-template-compiler": "^2.6.14"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tailwindcss/line-clamp": "^0.3.1",
|
||||
"@tailwindcss/forms": "^0.3.4",
|
||||
"@tailwindcss/line-clamp": "^0.3.1",
|
||||
"autoprefixer": "^10.3.7",
|
||||
"axios": "^0.25",
|
||||
"card-js": "^1.0.13",
|
||||
|
@ -15,6 +15,8 @@ namespace Tests\Feature\Notify;
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\Models\CompanyToken;
|
||||
use App\Models\CompanyUser;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\InvoiceInvitation;
|
||||
use App\Models\Product;
|
||||
use App\Models\User;
|
||||
use App\Utils\Traits\Notifications\UserNotifies;
|
||||
@ -42,6 +44,65 @@ class NotificationTest extends TestCase
|
||||
$this->makeTestData();
|
||||
}
|
||||
|
||||
|
||||
public function testEntityViewedNotificationWithEntityLate()
|
||||
{
|
||||
// ['all_notifications', 'all_user_notifications', 'invoice_created_user', 'invoice_sent_user', 'invoice_viewed_user', 'invoice_late_user'];
|
||||
$u = User::factory()->create([
|
||||
'account_id' => $this->account->id,
|
||||
'email' => $this->faker->safeEmail(),
|
||||
'confirmation_code' => uniqid("st",true),
|
||||
]);
|
||||
|
||||
$company_token = new CompanyToken;
|
||||
$company_token->user_id = $u->id;
|
||||
$company_token->company_id = $this->company->id;
|
||||
$company_token->account_id = $this->account->id;
|
||||
$company_token->name = 'test token';
|
||||
$company_token->token = Str::random(64);
|
||||
$company_token->is_system = true;
|
||||
$company_token->save();
|
||||
|
||||
$u->companies()->attach($this->company->id, [
|
||||
'account_id' => $this->account->id,
|
||||
'is_owner' => 1,
|
||||
'is_admin' => 1,
|
||||
'is_locked' => 0,
|
||||
'notifications' => CompanySettings::notificationDefaults(),
|
||||
'settings' => null,
|
||||
]);
|
||||
|
||||
$company_user = CompanyUser::where('user_id', $u->id)->where('company_id', $this->company->id)->first();
|
||||
|
||||
$notifications = new \stdClass;
|
||||
$notifications->email = ["invoice_late_user","quote_approved_user"];
|
||||
$company_user->update(['notifications' => (array)$notifications]);
|
||||
|
||||
$i = Invoice::factory()->create([
|
||||
'user_id' => $u->id,
|
||||
'company_id' => $this->company->id,
|
||||
'number' => uniqid("st",true),
|
||||
'client_id' => $this->client->id,
|
||||
]);
|
||||
|
||||
$invitation = InvoiceInvitation::factory()->create([
|
||||
'user_id' => $u->id,
|
||||
'company_id' => $this->company->id,
|
||||
'invoice_id' => $i->id,
|
||||
'client_contact_id' => $this->client->contacts->first()->id,
|
||||
]);
|
||||
|
||||
$methods = $this->findUserNotificationTypes($invitation, $company_user, 'invoice', ['all_notifications', 'invoice_late_user']);
|
||||
|
||||
$this->assertCount(1, $methods);
|
||||
|
||||
$methods = $this->findUserNotificationTypes($invitation, $company_user, 'invoice', ['all_notifications', 'invoice_viewed', 'invoice_viewed_all']);
|
||||
|
||||
$this->assertCount(0, $methods);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function testNotificationFound()
|
||||
{
|
||||
$notifications = new \stdClass;
|
||||
@ -101,7 +162,6 @@ class NotificationTest extends TestCase
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function testAllNotificationsDoesNotFiresForUser()
|
||||
{
|
||||
$u = User::factory()->create([
|
||||
@ -170,12 +230,13 @@ class NotificationTest extends TestCase
|
||||
'company_id' => $this->company->id
|
||||
]);
|
||||
|
||||
$methods = $this->findUserEntityNotificationType($p, $cu, []);
|
||||
$methods = $this->findUserEntityNotificationType($p, $cu, ['inventory_user']);
|
||||
|
||||
nlog($methods);
|
||||
|
||||
$this->assertCount(1, $methods);
|
||||
|
||||
$this->assertTrue($this->checkNotificationExists($cu, $p, ['inventory_all', 'inventory_user']));
|
||||
|
||||
}
|
||||
|
||||
|
@ -79,6 +79,20 @@ class PermissionsTest extends TestCase
|
||||
|
||||
}
|
||||
|
||||
public function testViewClientPermission()
|
||||
{
|
||||
|
||||
$low_cu = CompanyUser::where(['company_id' => $this->company->id, 'user_id' => $this->user->id])->first();
|
||||
$low_cu->permissions = '["view_client"]';
|
||||
$low_cu->save();
|
||||
|
||||
$this->assertFalse($this->user->hasPermission("viewclient"));
|
||||
|
||||
// this is aberrant
|
||||
$this->assertFalse($this->user->hasPermission("view____client"));
|
||||
|
||||
}
|
||||
|
||||
public function testPermissionResolution()
|
||||
{
|
||||
$class = 'view'.lcfirst(class_basename(\Illuminate\Support\Str::snake(Invoice::class)));
|
||||
@ -162,7 +176,6 @@ class PermissionsTest extends TestCase
|
||||
public function testReturnTypesOfStripos()
|
||||
{
|
||||
|
||||
|
||||
$this->assertEquals(0, stripos("view_client", ''));
|
||||
|
||||
$all_permission = '[]';
|
||||
@ -193,17 +206,7 @@ class PermissionsTest extends TestCase
|
||||
|
||||
}
|
||||
|
||||
public function testViewClientPermission()
|
||||
{
|
||||
|
||||
$low_cu = CompanyUser::where(['company_id' => $this->company->id, 'user_id' => $this->user->id])->first();
|
||||
$low_cu->permissions = '["view_client"]';
|
||||
$low_cu->save();
|
||||
|
||||
// this is aberrant
|
||||
$this->assertFalse($this->user->hasPermission("view____client"));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
43
tests/cypress/integration/login.cy.js
vendored
Normal file
43
tests/cypress/integration/login.cy.js
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
describe('Test Login Page', () => {
|
||||
|
||||
it('Shows the Password Reset Pasge.', () => {
|
||||
|
||||
|
||||
cy.visit('/client/password/reset');
|
||||
cy.contains('Password Recovery');
|
||||
|
||||
cy.get('input[name=email]').type('cypress@example.com{enter}');
|
||||
cy.contains('We have e-mailed your password reset link!');
|
||||
|
||||
cy.visit('/client/password/reset');
|
||||
cy.contains('Password Recovery');
|
||||
|
||||
cy.get('input[name=email]').type('nono@example.com{enter}');
|
||||
cy.contains("We can't find a user with that e-mail address.");
|
||||
|
||||
});
|
||||
|
||||
it('Shows the login page.', () => {
|
||||
|
||||
cy.visit('/client/login');
|
||||
cy.contains('Client Portal');
|
||||
|
||||
cy.get('input[name=email]').type('cypress@example.com');
|
||||
cy.get('input[name=password]').type('password{enter}');
|
||||
cy.url().should('include', '/invoices');
|
||||
|
||||
cy.visit('/client/recurring_invoices').contains('Recurring Invoices');
|
||||
cy.visit('/client/payments').contains('Payments');
|
||||
cy.visit('/client/quotes').contains('Quotes');
|
||||
cy.visit('/client/credits').contains('Credits');
|
||||
cy.visit('/client/payment_methods').contains('Payment Methods');
|
||||
cy.visit('/client/documents').contains('Documents');
|
||||
cy.visit('/client/statement').contains('Statement');
|
||||
cy.visit('/client/subscriptions').contains('Subscriptions');
|
||||
|
||||
cy.get('[data-ref="client-profile-dropdown"]').click();
|
||||
cy.get('[data-ref="client-profile-dropdown-settings"]').click();
|
||||
cy.contains('Client Information');
|
||||
});
|
||||
|
||||
});
|
23
tests/cypress/plugins/index.js
vendored
Normal file
23
tests/cypress/plugins/index.js
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
/// <reference types="cypress" />
|
||||
// ***********************************************************
|
||||
// This example plugins/index.js can be used to load plugins
|
||||
//
|
||||
// You can change the location of this file or turn off loading
|
||||
// the plugins file with the 'pluginsFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/plugins-guide
|
||||
// ***********************************************************
|
||||
|
||||
// This function is called when a project is opened or re-opened (e.g. due to
|
||||
// the project's config changing)
|
||||
|
||||
/**
|
||||
* @type {Cypress.PluginConfig}
|
||||
*/
|
||||
module.exports = (on, config) => {
|
||||
// `on` is used to hook into various events Cypress emits
|
||||
// `config` is the resolved Cypress config
|
||||
|
||||
on('task', require('./swap-env'));
|
||||
};
|
21
tests/cypress/plugins/swap-env.js
vendored
Normal file
21
tests/cypress/plugins/swap-env.js
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
let fs = require('fs');
|
||||
|
||||
module.exports = {
|
||||
activateCypressEnvFile() {
|
||||
if (fs.existsSync('.env.cypress')) {
|
||||
fs.renameSync('.env', '.env.backup');
|
||||
fs.renameSync('.env.cypress', '.env');
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
activateLocalEnvFile() {
|
||||
if (fs.existsSync('.env.backup')) {
|
||||
fs.renameSync('.env', '.env.cypress');
|
||||
fs.renameSync('.env.backup', '.env');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
3
tests/cypress/support/assertions.js
vendored
Normal file
3
tests/cypress/support/assertions.js
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
Cypress.Commands.add('assertRedirect', path => {
|
||||
cy.location('pathname').should('eq', `/${path}`.replace(/^\/\//, '/'));
|
||||
});
|
92
tests/cypress/support/index.d.ts
vendored
Normal file
92
tests/cypress/support/index.d.ts
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable<Subject> {
|
||||
/**
|
||||
* Log in the user with the given attributes, or create a new user and then log them in.
|
||||
*
|
||||
* @example
|
||||
* cy.login()
|
||||
* cy.login({ id: 1 })
|
||||
*/
|
||||
login(attributes?: object): Chainable<any>;
|
||||
|
||||
/**
|
||||
* Log out the current user.
|
||||
*
|
||||
* @example
|
||||
* cy.logout()
|
||||
*/
|
||||
logout(): Chainable<any>;
|
||||
|
||||
/**
|
||||
* Fetch the currently authenticated user.
|
||||
*
|
||||
* @example
|
||||
* cy.currentUser()
|
||||
*/
|
||||
currentUser(): Chainable<any>;
|
||||
|
||||
/**
|
||||
* Fetch a CSRF token from the server.
|
||||
*
|
||||
* @example
|
||||
* cy.logout()
|
||||
*/
|
||||
csrfToken(): Chainable<any>;
|
||||
|
||||
/**
|
||||
* Fetch a fresh list of URI routes from the server.
|
||||
*
|
||||
* @example
|
||||
* cy.logout()
|
||||
*/
|
||||
refreshRoutes(): Chainable<any>;
|
||||
|
||||
/**
|
||||
* Create and persist a new Eloquent record using Laravel model factories.
|
||||
*
|
||||
* @example
|
||||
* cy.create('App\\User');
|
||||
* cy.create('App\\User', 2);
|
||||
* cy.create('App\\User', 2, { active: false });
|
||||
* cy.create({ model: 'App\\User', state: ['guest'], relations: ['profile'], count: 2 }
|
||||
*/
|
||||
create(): Chainable<any>;
|
||||
|
||||
/**
|
||||
* Refresh the database state using Laravel's migrate:fresh command.
|
||||
*
|
||||
* @example
|
||||
* cy.refreshDatabase()
|
||||
* cy.refreshDatabase({ '--drop-views': true }
|
||||
*/
|
||||
refreshDatabase(options?: object): Chainable<any>;
|
||||
|
||||
/**
|
||||
* Run Artisan's db:seed command.
|
||||
*
|
||||
* @example
|
||||
* cy.seed()
|
||||
* cy.seed('PlansTableSeeder')
|
||||
*/
|
||||
seed(seederClass?: string): Chainable<any>;
|
||||
|
||||
/**
|
||||
* Run an Artisan command.
|
||||
*
|
||||
* @example
|
||||
* cy.artisan()
|
||||
*/
|
||||
artisan(command: string, parameters?: object, options?: object): Chainable<any>;
|
||||
|
||||
/**
|
||||
* Execute arbitrary PHP on the server.
|
||||
*
|
||||
* @example
|
||||
* cy.php('2 + 2')
|
||||
* cy.php('App\\User::count()')
|
||||
*/
|
||||
php(command: string): Chainable<any>;
|
||||
}
|
||||
}
|
32
tests/cypress/support/index.js
vendored
Normal file
32
tests/cypress/support/index.js
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
/// <reference types="./" />
|
||||
|
||||
import './laravel-commands';
|
||||
import './laravel-routes';
|
||||
import './assertions';
|
||||
|
||||
before(() => {
|
||||
cy.task('activateCypressEnvFile', {}, { log: false });
|
||||
cy.artisan('config:clear', {}, { log: false });
|
||||
cy.refreshRoutes();
|
||||
cy.seed("RandomDataSeeder");
|
||||
});
|
||||
|
||||
after(() => {
|
||||
cy.task('activateLocalEnvFile', {}, { log: false });
|
||||
cy.artisan('config:clear', {}, { log: false });
|
||||
});
|
301
tests/cypress/support/laravel-commands.js
vendored
Normal file
301
tests/cypress/support/laravel-commands.js
vendored
Normal file
@ -0,0 +1,301 @@
|
||||
/**
|
||||
* Create a new user and log them in.
|
||||
*
|
||||
* @param {Object} attributes
|
||||
*
|
||||
* @example cy.login();
|
||||
* cy.login({ name: 'JohnDoe' });
|
||||
* cy.login({ attributes: { name: 'JohnDoe' }, state: 'guest', load: ['comments] });
|
||||
*/
|
||||
Cypress.Commands.add('login', (attributes = {}) => {
|
||||
// Are we using the new object system.
|
||||
let requestBody = attributes.attributes || attributes.state || attributes.load ? attributes : { attributes };
|
||||
|
||||
return cy
|
||||
.csrfToken()
|
||||
.then((token) => {
|
||||
return cy.request({
|
||||
method: 'POST',
|
||||
url: '/__cypress__/login',
|
||||
body: { ...requestBody, _token: token },
|
||||
log: false,
|
||||
});
|
||||
})
|
||||
.then(({ body }) => {
|
||||
Cypress.Laravel.currentUser = body;
|
||||
|
||||
Cypress.log({
|
||||
name: 'login',
|
||||
message: JSON.stringify(body),
|
||||
consoleProps: () => ({ user: body }),
|
||||
});
|
||||
})
|
||||
.its('body', { log: false });
|
||||
});
|
||||
|
||||
/**
|
||||
* Fetch the currently authenticated user object.
|
||||
*
|
||||
* @example cy.currentUser();
|
||||
*/
|
||||
Cypress.Commands.add('currentUser', () => {
|
||||
return cy.csrfToken().then((token) => {
|
||||
return cy
|
||||
.request({
|
||||
method: 'POST',
|
||||
url: '/__cypress__/current-user',
|
||||
body: { _token: token },
|
||||
log: false,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.body) {
|
||||
cy.log('No authenticated user found.');
|
||||
}
|
||||
|
||||
Cypress.Laravel.currentUser = response?.body;
|
||||
|
||||
return response?.body;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Logout the current user.
|
||||
*
|
||||
* @example cy.logout();
|
||||
*/
|
||||
Cypress.Commands.add('logout', () => {
|
||||
return cy
|
||||
.csrfToken()
|
||||
.then((token) => {
|
||||
return cy.request({
|
||||
method: 'POST',
|
||||
url: '/__cypress__/logout',
|
||||
body: { _token: token },
|
||||
log: false,
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
Cypress.log({ name: 'logout', message: '' });
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Fetch a CSRF token.
|
||||
*
|
||||
* @example cy.csrfToken();
|
||||
*/
|
||||
Cypress.Commands.add('csrfToken', () => {
|
||||
return cy
|
||||
.request({
|
||||
method: 'GET',
|
||||
url: '/__cypress__/csrf_token',
|
||||
log: false,
|
||||
})
|
||||
.its('body', { log: false });
|
||||
});
|
||||
|
||||
/**
|
||||
* Fetch and store all named routes.
|
||||
*
|
||||
* @example cy.refreshRoutes();
|
||||
*/
|
||||
Cypress.Commands.add('refreshRoutes', () => {
|
||||
return cy.csrfToken().then((token) => {
|
||||
return cy
|
||||
.request({
|
||||
method: 'POST',
|
||||
url: '/__cypress__/routes',
|
||||
body: { _token: token },
|
||||
log: false,
|
||||
})
|
||||
.its('body', { log: false })
|
||||
.then((routes) => {
|
||||
cy.writeFile(Cypress.config().supportFolder + '/routes.json', routes, {
|
||||
log: false,
|
||||
});
|
||||
|
||||
Cypress.Laravel.routes = routes;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Visit the given URL or route.
|
||||
*
|
||||
* @example cy.visit('foo/path');
|
||||
* cy.visit({ route: 'home' });
|
||||
* cy.visit({ route: 'team', parameters: { team: 1 } });
|
||||
*/
|
||||
Cypress.Commands.overwrite('visit', (originalFn, subject, options) => {
|
||||
if (subject.route) {
|
||||
return originalFn({
|
||||
url: Cypress.Laravel.route(subject.route, subject.parameters || {}),
|
||||
method: Cypress.Laravel.routes[subject.route].method[0],
|
||||
...options
|
||||
});
|
||||
}
|
||||
|
||||
return originalFn(subject, options);
|
||||
});
|
||||
|
||||
/**
|
||||
* Create a new Eloquent factory.
|
||||
*
|
||||
* @param {String} model
|
||||
* @param {Number|null} times
|
||||
* @param {Object} attributes
|
||||
*
|
||||
* @example cy.create('App\\User');
|
||||
* cy.create('App\\User', 2);
|
||||
* cy.create('App\\User', 2, { active: false });
|
||||
* cy.create('App\\User', { active: false });
|
||||
* cy.create('App\\User', 2, { active: false });
|
||||
* cy.create('App\\User', 2, { active: false }, ['profile']);
|
||||
* cy.create('App\\User', 2, { active: false }, ['profile'], ['guest']);
|
||||
* cy.create('App\\User', { active: false }, ['profile']);
|
||||
* cy.create('App\\User', { active: false }, ['profile'], ['guest']);
|
||||
* cy.create('App\\User', ['profile']);
|
||||
* cy.create('App\\User', ['profile'], ['guest']);
|
||||
* cy.create({ model: 'App\\User', state: ['guest'], relations: ['profile'], count: 2 }
|
||||
*/
|
||||
Cypress.Commands.add('create', (model, count = 1, attributes = {}, load = [], state = []) => {
|
||||
let requestBody = {};
|
||||
|
||||
if (typeof model !== 'object') {
|
||||
if (Array.isArray(count)) {
|
||||
state = attributes;
|
||||
attributes = {};
|
||||
load = count;
|
||||
count = 1;
|
||||
}
|
||||
|
||||
if (typeof count === 'object') {
|
||||
state = load;
|
||||
load = attributes;
|
||||
attributes = count;
|
||||
count = 1;
|
||||
}
|
||||
|
||||
requestBody = { model, state, attributes, load, count };
|
||||
} else {
|
||||
requestBody = model;
|
||||
}
|
||||
|
||||
return cy
|
||||
.csrfToken()
|
||||
.then((token) => {
|
||||
return cy.request({
|
||||
method: 'POST',
|
||||
url: '/__cypress__/factory',
|
||||
body: { ...requestBody, _token: token },
|
||||
log: false,
|
||||
});
|
||||
})
|
||||
.then((response) => {
|
||||
Cypress.log({
|
||||
name: 'create',
|
||||
message: requestBody.model + (requestBody.count > 1 ? ` (${requestBody.count} times)` : ''),
|
||||
consoleProps: () => ({ [model]: response.body }),
|
||||
});
|
||||
})
|
||||
.its('body', { log: false });
|
||||
});
|
||||
|
||||
/**
|
||||
* Refresh the database state.
|
||||
*
|
||||
* @param {Object} options
|
||||
*
|
||||
* @example cy.refreshDatabase();
|
||||
* cy.refreshDatabase({ '--drop-views': true });
|
||||
*/
|
||||
Cypress.Commands.add('refreshDatabase', (options = {}) => {
|
||||
return cy.artisan('migrate:fresh', options);
|
||||
});
|
||||
|
||||
/**
|
||||
* Seed the database.
|
||||
*
|
||||
* @param {String} seederClass
|
||||
*
|
||||
* @example cy.seed();
|
||||
* cy.seed('PlansTableSeeder');
|
||||
*/
|
||||
Cypress.Commands.add('seed', (seederClass = '') => {
|
||||
let options = {};
|
||||
|
||||
if (seederClass) {
|
||||
options['--class'] = seederClass;
|
||||
}
|
||||
|
||||
return cy.artisan('db:seed', options);
|
||||
});
|
||||
|
||||
/**
|
||||
* Trigger an Artisan command.
|
||||
*
|
||||
* @param {String} command
|
||||
* @param {Object} parameters
|
||||
* @param {Object} options
|
||||
*
|
||||
* @example cy.artisan('cache:clear');
|
||||
*/
|
||||
Cypress.Commands.add('artisan', (command, parameters = {}, options = {}) => {
|
||||
options = Object.assign({}, { log: true }, options);
|
||||
|
||||
if (options.log) {
|
||||
Cypress.log({
|
||||
name: 'artisan',
|
||||
message: (() => {
|
||||
let message = command;
|
||||
|
||||
for (let key in parameters) {
|
||||
message += ` ${key}="${parameters[key]}"`;
|
||||
}
|
||||
|
||||
return message;
|
||||
})(),
|
||||
consoleProps: () => ({ command, parameters }),
|
||||
});
|
||||
}
|
||||
|
||||
return cy.csrfToken().then((token) => {
|
||||
return cy.request({
|
||||
method: 'POST',
|
||||
url: '/__cypress__/artisan',
|
||||
body: { command: command, parameters: parameters, _token: token },
|
||||
log: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Execute arbitrary PHP.
|
||||
*
|
||||
* @param {String} command
|
||||
*
|
||||
* @example cy.php('2 + 2');
|
||||
* cy.php('App\\User::count()');
|
||||
*/
|
||||
Cypress.Commands.add('php', (command) => {
|
||||
return cy
|
||||
.csrfToken()
|
||||
.then((token) => {
|
||||
return cy.request({
|
||||
method: 'POST',
|
||||
url: '/__cypress__/run-php',
|
||||
body: { command: command, _token: token },
|
||||
log: false,
|
||||
});
|
||||
})
|
||||
.then((response) => {
|
||||
Cypress.log({
|
||||
name: 'php',
|
||||
message: command,
|
||||
consoleProps: () => ({ result: response.body.result }),
|
||||
});
|
||||
})
|
||||
.its('body.result', { log: false });
|
||||
});
|
21
tests/cypress/support/laravel-routes.js
vendored
Normal file
21
tests/cypress/support/laravel-routes.js
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
Cypress.Laravel = {
|
||||
routes: {},
|
||||
|
||||
route: (name, parameters = {}) => {
|
||||
assert(
|
||||
Cypress.Laravel.routes.hasOwnProperty(name),
|
||||
`Laravel route "${name}" does not exist.`
|
||||
);
|
||||
|
||||
return ((uri) => {
|
||||
Object.keys(parameters).forEach((parameter) => {
|
||||
uri = uri.replace(
|
||||
new RegExp(`{${parameter}}`),
|
||||
parameters[parameter]
|
||||
);
|
||||
});
|
||||
|
||||
return uri;
|
||||
})(Cypress.Laravel.routes[name].uri);
|
||||
},
|
||||
};
|
5608
tests/cypress/support/routes.json
Normal file
5608
tests/cypress/support/routes.json
Normal file
File diff suppressed because it is too large
Load Diff
BIN
tests/cypress/videos/login.cy.js.mp4
Normal file
BIN
tests/cypress/videos/login.cy.js.mp4
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user