diff --git a/app/Console/Commands/CreateSingleAccount.php b/app/Console/Commands/CreateSingleAccount.php index 82d738b16b35..b53626595059 100644 --- a/app/Console/Commands/CreateSingleAccount.php +++ b/app/Console/Commands/CreateSingleAccount.php @@ -14,9 +14,11 @@ namespace App\Console\Commands; use App\DataMapper\CompanySettings; use App\DataMapper\FeesAndLimits; use App\Events\Invoice\InvoiceWasCreated; +use App\Events\RecurringInvoice\RecurringInvoiceWasCreated; use App\Factory\GroupSettingFactory; use App\Factory\InvoiceFactory; use App\Factory\InvoiceItemFactory; +use App\Factory\RecurringInvoiceFactory; use App\Factory\SubscriptionFactory; use App\Helpers\Invoice\InvoiceSum; use App\Jobs\Company\CreateCompanyTaskStatuses; @@ -48,6 +50,7 @@ use Illuminate\Console\Command; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Schema; use Illuminate\Support\Str; +use stdClass; class CreateSingleAccount extends Command { @@ -117,7 +120,7 @@ class CreateSingleAccount extends Command $company->settings = $settings; $company->save(); - + $account->default_company_id = $company->id; $account->save(); @@ -165,7 +168,7 @@ class CreateSingleAccount extends Command TaxRate::factory()->create([ 'user_id' => $user->id, - 'company_id' => $company->id, + 'company_id' => $company->id, 'name' => 'VAT', 'rate' => 17.5 ]); @@ -176,7 +179,7 @@ class CreateSingleAccount extends Command 'name' => 'CA Sales Tax', 'rate' => 5 ]); - + $this->info('Creating '.$this->count.' clients'); @@ -225,16 +228,19 @@ class CreateSingleAccount extends Command $client = $company->clients->random(); - $this->info('creating task for client #'.$client->id); + $this->info('creating task for client #' . $client->id); $this->createTask($client); $client = $company->clients->random(); - $this->info('creating project for client #'.$client->id); + $this->info('creating project for client #' . $client->id); $this->createProject($client); - $this->info('creating credit for client #'.$client->id); + $this->info('creating credit for client #' . $client->id); $this->createCredit($client); + + $this->info('creating recurring invoice for client # ' . $client->id); + $this->createRecurringInvoice($client); } $this->createGateways($company, $user); @@ -249,34 +255,34 @@ class CreateSingleAccount extends Command $gs->save(); $p1 = Product::factory()->create([ - 'user_id' => $user->id, - 'company_id' => $company->id, - 'product_key' => 'pro_plan', - 'notes' => 'The Pro Plan', - 'cost' => 10, - 'price' => 10, - 'quantity' => 1, - ]); + 'user_id' => $user->id, + 'company_id' => $company->id, + 'product_key' => 'pro_plan', + 'notes' => 'The Pro Plan', + 'cost' => 10, + 'price' => 10, + 'quantity' => 1, + ]); $p2 = Product::factory()->create([ - 'user_id' => $user->id, - 'company_id' => $company->id, - 'product_key' => 'enterprise_plan', - 'notes' => 'The Enterprise Plan', - 'cost' => 14, - 'price' => 14, - 'quantity' => 1, - ]); + 'user_id' => $user->id, + 'company_id' => $company->id, + 'product_key' => 'enterprise_plan', + 'notes' => 'The Enterprise Plan', + 'cost' => 14, + 'price' => 14, + 'quantity' => 1, + ]); $p3 = Product::factory()->create([ - 'user_id' => $user->id, - 'company_id' => $company->id, - 'product_key' => 'free_plan', - 'notes' => 'The Free Plan', - 'cost' => 0, - 'price' => 0, - 'quantity' => 1, - ]); + 'user_id' => $user->id, + 'company_id' => $company->id, + 'product_key' => 'free_plan', + 'notes' => 'The Free Plan', + 'cost' => 0, + 'price' => 0, + 'quantity' => 1, + ]); $webhook_config = [ 'post_purchase_url' => 'http://ninja.test:8000/api/admin/plan', @@ -435,6 +441,10 @@ class CreateSingleAccount extends Command $invoice = $invoice_calc->getInvoice(); + if ($this->gateway === 'braintree') { + $invoice->amount = 100; // Braintree sandbox only allows payments under 2,000 to complete successfully. + } + $invoice->save(); $invoice->service()->createInvitations()->markSent(); @@ -619,7 +629,7 @@ class CreateSingleAccount extends Command $gateway_types = $cg->driver(new Client)->gatewayTypes(); - $fees_and_limits = new \stdClass; + $fees_and_limits = new stdClass; $fees_and_limits->{$gateway_types[0]} = new FeesAndLimits; $cg->fees_and_limits = $fees_and_limits; @@ -642,7 +652,7 @@ class CreateSingleAccount extends Command $gateway_types = $cg->driver(new Client)->gatewayTypes(); - $fees_and_limits = new \stdClass; + $fees_and_limits = new stdClass; $fees_and_limits->{$gateway_types[0]} = new FeesAndLimits; $cg->fees_and_limits = $fees_and_limits; @@ -663,7 +673,7 @@ class CreateSingleAccount extends Command $gateway_types = $cg->driver(new Client)->gatewayTypes(); - $fees_and_limits = new \stdClass; + $fees_and_limits = new stdClass; $fees_and_limits->{$gateway_types[0]} = new FeesAndLimits; $cg->fees_and_limits = $fees_and_limits; @@ -684,11 +694,96 @@ class CreateSingleAccount extends Command $gateway_types = $cg->driver(new Client)->gatewayTypes(); - $fees_and_limits = new \stdClass; + $fees_and_limits = new stdClass; + $fees_and_limits->{$gateway_types[0]} = new FeesAndLimits; + + $cg->fees_and_limits = $fees_and_limits; + $cg->save(); + } + + if (config('ninja.testvars.wepay') && ($this->gateway == 'all' || $this->gateway == 'wepay')) { + $cg = new CompanyGateway; + $cg->company_id = $company->id; + $cg->user_id = $user->id; + $cg->gateway_key = '8fdeed552015b3c7b44ed6c8ebd9e992'; + $cg->require_cvv = true; + $cg->require_billing_address = true; + $cg->require_shipping_address = true; + $cg->update_details = true; + $cg->config = encrypt(config('ninja.testvars.wepay')); + $cg->save(); + + $gateway_types = $cg->driver(new Client)->gatewayTypes(); + + $fees_and_limits = new stdClass; + $fees_and_limits->{$gateway_types[0]} = new FeesAndLimits; + + $cg->fees_and_limits = $fees_and_limits; + $cg->save(); + } + + if (config('ninja.testvars.braintree') && ($this->gateway == 'all' || $this->gateway == 'braintree')) { + $cg = new CompanyGateway; + $cg->company_id = $company->id; + $cg->user_id = $user->id; + $cg->gateway_key = 'f7ec488676d310683fb51802d076d713'; + $cg->require_cvv = true; + $cg->require_billing_address = true; + $cg->require_shipping_address = true; + $cg->update_details = true; + $cg->config = encrypt(config('ninja.testvars.braintree')); + $cg->save(); + + $gateway_types = $cg->driver(new Client)->gatewayTypes(); + + $fees_and_limits = new stdClass; $fees_and_limits->{$gateway_types[0]} = new FeesAndLimits; $cg->fees_and_limits = $fees_and_limits; $cg->save(); } } + + private function createRecurringInvoice($client) + { + $faker = Factory::create(); + + $invoice = RecurringInvoiceFactory::create($client->company->id, $client->user->id); //stub the company and user_id + $invoice->client_id = $client->id; + $dateable = Carbon::now()->subDays(rand(0, 90)); + $invoice->date = $dateable; + + $invoice->line_items = $this->buildLineItems(rand(1, 10)); + $invoice->uses_inclusive_taxes = false; + + if (rand(0, 1)) { + $invoice->tax_name1 = 'GST'; + $invoice->tax_rate1 = 10.00; + } + + if (rand(0, 1)) { + $invoice->tax_name2 = 'VAT'; + $invoice->tax_rate2 = 17.50; + } + + if (rand(0, 1)) { + $invoice->tax_name3 = 'CA Sales Tax'; + $invoice->tax_rate3 = 5; + } + + $invoice->custom_value1 = $faker->date; + $invoice->custom_value2 = rand(0, 1) ? 'yes' : 'no'; + + $invoice->status_id = RecurringInvoice::STATUS_ACTIVE; + $invoice->save(); + + $invoice_calc = new InvoiceSum($invoice); + $invoice_calc->build(); + + $invoice = $invoice_calc->getInvoice(); + + $invoice->save(); + + event(new RecurringInvoiceWasCreated($invoice, $invoice->company, Ninja::eventVars())); + } } diff --git a/app/Http/Livewire/Profile/Settings/General.php b/app/Http/Livewire/Profile/Settings/General.php index e8a809acaf95..4b333431a671 100644 --- a/app/Http/Livewire/Profile/Settings/General.php +++ b/app/Http/Livewire/Profile/Settings/General.php @@ -32,6 +32,7 @@ class General extends Component 'first_name' => ['sometimes'], 'last_name' => ['sometimes'], 'email' => ['required', 'email'], + 'phone' => ['sometimes'], ]; public function mount() diff --git a/composer.json b/composer.json index 056dee862f67..151dd8239ab6 100644 --- a/composer.json +++ b/composer.json @@ -87,6 +87,7 @@ "fakerphp/faker": "^1.14", "filp/whoops": "^2.7", "friendsofphp/php-cs-fixer": "^2.16", + "laravel/dusk": "^6.15", "mockery/mockery": "^1.3.1", "nunomaduro/collision": "^5.0", "phpunit/phpunit": "^9.0", diff --git a/composer.lock b/composer.lock index 99a3b5134fb9..15b58365b9a7 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "25a0cbf18fc238305c7ea640c49ba89a", + "content-hash": "d2beb37ff5fbee59ad4bb792e944eb10", "packages": [ { "name": "asm/php-ansible", @@ -11950,6 +11950,79 @@ }, "time": "2020-07-09T08:09:16+00:00" }, + { + "name": "laravel/dusk", + "version": "v6.15.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/dusk.git", + "reference": "45b55fa20321086c4f8cc4e712cbe54db644e21c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/dusk/zipball/45b55fa20321086c4f8cc4e712cbe54db644e21c", + "reference": "45b55fa20321086c4f8cc4e712cbe54db644e21c", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-zip": "*", + "illuminate/console": "^6.0|^7.0|^8.0", + "illuminate/support": "^6.0|^7.0|^8.0", + "nesbot/carbon": "^2.0", + "php": "^7.2|^8.0", + "php-webdriver/webdriver": "^1.9.0", + "symfony/console": "^4.3|^5.0", + "symfony/finder": "^4.3|^5.0", + "symfony/process": "^4.3|^5.0", + "vlucas/phpdotenv": "^3.0|^4.0|^5.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "^4.16|^5.17.1|^6.12.1", + "phpunit/phpunit": "^7.5.15|^8.4|^9.0" + }, + "suggest": { + "ext-pcntl": "Used to gracefully terminate Dusk when tests are running." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Dusk\\DuskServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Dusk\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel Dusk provides simple end-to-end testing and browser automation.", + "keywords": [ + "laravel", + "testing", + "webdriver" + ], + "support": { + "issues": "https://github.com/laravel/dusk/issues", + "source": "https://github.com/laravel/dusk/tree/v6.15.0" + }, + "time": "2021-04-06T14:14:57+00:00" + }, { "name": "maximebf/debugbar", "version": "v1.16.5", @@ -12503,6 +12576,72 @@ }, "time": "2020-10-14T08:39:05+00:00" }, + { + "name": "php-webdriver/webdriver", + "version": "1.11.1", + "source": { + "type": "git", + "url": "https://github.com/php-webdriver/php-webdriver.git", + "reference": "da16e39968f8dd5cfb7d07eef91dc2b731c69880" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/da16e39968f8dd5cfb7d07eef91dc2b731c69880", + "reference": "da16e39968f8dd5cfb7d07eef91dc2b731c69880", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-zip": "*", + "php": "^5.6 || ~7.0 || ^8.0", + "symfony/polyfill-mbstring": "^1.12", + "symfony/process": "^2.8 || ^3.1 || ^4.0 || ^5.0" + }, + "replace": { + "facebook/webdriver": "*" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.0", + "ondram/ci-detector": "^2.1 || ^3.5 || ^4.0", + "php-coveralls/php-coveralls": "^2.4", + "php-mock/php-mock-phpunit": "^1.1 || ^2.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpunit/phpunit": "^5.7 || ^7 || ^8 || ^9", + "squizlabs/php_codesniffer": "^3.5", + "symfony/var-dumper": "^3.3 || ^4.0 || ^5.0" + }, + "suggest": { + "ext-SimpleXML": "For Firefox profile creation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Facebook\\WebDriver\\": "lib/" + }, + "files": [ + "lib/Exception/TimeoutException.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP client for Selenium WebDriver. Previously facebook/webdriver.", + "homepage": "https://github.com/php-webdriver/php-webdriver", + "keywords": [ + "Chromedriver", + "geckodriver", + "php", + "selenium", + "webdriver" + ], + "support": { + "issues": "https://github.com/php-webdriver/php-webdriver/issues", + "source": "https://github.com/php-webdriver/php-webdriver/tree/1.11.1" + }, + "time": "2021-05-21T15:12:49+00:00" + }, { "name": "phpdocumentor/reflection-common", "version": "2.2.0", @@ -14742,5 +14881,5 @@ "platform-dev": { "php": "^7.3|^7.4|^8.0" }, - "plugin-api-version": "2.0.0" + "plugin-api-version": "2.1.0" } diff --git a/config/ninja.php b/config/ninja.php index 8d2e235be7e0..5c20700d01e4 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -82,6 +82,8 @@ return [ 'checkout' => env('CHECKOUT_KEYS', ''), 'travis' => env('TRAVIS', false), 'test_email' => env('TEST_EMAIL', 'test@example.com'), + 'wepay' => env('WEPAY_KEYS', ''), + 'braintree' => env('BRAINTREE_KEYS', ''), ], 'contact' => [ 'email' => env('MAIL_FROM_ADDRESS'), diff --git a/cypress.json b/cypress.json deleted file mode 100644 index fa8e22edf8d1..000000000000 --- a/cypress.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "video": false, - "baseUrl": "http://localhost:8080/", - "chromeWebSecurity": false, - "env": { - "runningEnvironment": "docker" - }, - "viewportWidth": 1280, - "viewportHeight": 800 -} diff --git a/cypress/excluded/checkout_credit_card.spec.js b/cypress/excluded/checkout_credit_card.spec.js deleted file mode 100644 index d0a6ab0325c4..000000000000 --- a/cypress/excluded/checkout_credit_card.spec.js +++ /dev/null @@ -1,48 +0,0 @@ -import { second } from '../fixtures/example.json'; - -describe('Checkout Credit Card Payments', () => { - beforeEach(() => { - // cy.useGateway(second); - cy.clientLogin(); - }); - - it('should be able to complete payment using checkout credit card', () => { - cy.visit('/client/invoices'); - - cy.get('#unpaid-checkbox').click(); - - cy.get('[data-cy=pay-now') - .first() - .click(); - - cy.location('pathname').should('eq', '/client/invoices/payment'); - - cy.get('[data-cy=payment-methods-dropdown').click(); - - cy.get('[data-cy=payment-method') - .first() - .click(); - - cy.wait(8000); - - cy.get('.cko-pay-now.show') - .first() - .click(); - - cy.wait(3000); - - cy.getWithinIframe('[data-checkout="card-number"]').type( - '4242424242424242' - ); - cy.getWithinIframe('[data-checkout="expiry-month"]').type('12'); - cy.getWithinIframe('[data-checkout="expiry-year"]').type('30'); - cy.getWithinIframe('[data-checkout="cvv"]').type('100'); - - cy.getWithinIframe('.form-submit') - .first() - .click(); - - cy.wait(5000); - cy.url().should('contain', '/client/payments'); - }); -}); diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json deleted file mode 100644 index f0300d2d471c..000000000000 --- a/cypress/fixtures/example.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "Using fixtures to represent data", - "email": "hello@cypress.io", - "body": "Fixtures are a great way to mock data for responses to routes", - - "first": "VolejRejNm", - "second": "Wpmbk5ezJn", - - "url": "http://localhost:8000" -} diff --git a/cypress/integration/client_portal/credits.spec.js b/cypress/integration/client_portal/credits.spec.js deleted file mode 100644 index ca51088147dd..000000000000 --- a/cypress/integration/client_portal/credits.spec.js +++ /dev/null @@ -1,37 +0,0 @@ -describe('Credits', () => { - beforeEach(() => { - cy.clientLogin(); - }); - - it('should show credits page', () => { - cy.visit('/client/credits'); - cy.location().should(location => { - expect(location.pathname).to.eq('/client/credits'); - }); - }); - - it('should show credits text', () => { - cy.visit('/client/credits'); - - cy.get('body') - .find('[data-ref=meta-title]') - .first() - .should('contain.text', 'Credits'); - }); - - /* it('should have required table elements', () => { - cy.visit('/client/credits'); - - cy.get('body') - .find('table.credits-table > tbody > tr') - .first() - .find('a') - .first() - .should('contain.text', 'View') - .click() - .location() - .should(location => { - expect(location.pathname).to.eq('/client/credits/VolejRejNm'); - }); - });*/ -}); diff --git a/cypress/integration/client_portal/invoices.spec.js b/cypress/integration/client_portal/invoices.spec.js deleted file mode 100644 index 8f1c9d28339d..000000000000 --- a/cypress/integration/client_portal/invoices.spec.js +++ /dev/null @@ -1,73 +0,0 @@ -context('Invoices', () => { - beforeEach(() => { - cy.clientLogin(); - }); - - it('should show invoices page', () => { - cy.visit('/client/invoices'); - cy.location().should(location => { - expect(location.pathname).to.eq('/client/invoices'); - }); - }); - - it('should show invoices text', () => { - cy.visit('/client/invoices'); - - cy.get('body') - .find('[data-ref=meta-title]') - .first() - .should('contain.text', 'Invoices'); - }); - - it('should show download and pay now buttons', () => { - cy.visit('/client/invoices'); - - cy.get('body') - .find('button[value="download"]') - .first() - .should('contain.text', 'Download'); - - cy.get('body') - .find('button[value="payment"]') - .first() - .should('contain.text', 'Pay Now'); - }); - - it('should have per page options dropdown', () => { - cy.visit('/client/invoices'); - - cy.get('body') - .find('select') - .first() - .should('have.value', '10'); - }); - - it('should have required table elements', () => { - cy.visit('/client/invoices'); - - cy.get('body') - .find('table.invoices-table > tbody > tr') - .first() - .find('.button-link') - .first() - .should('contain.text', 'View') - .click() - .location() - .should(location => { - expect(location.pathname).to.eq('/client/invoices/VolejRejNm'); - }); - }); - - it('should filter table content', () => { - cy.visit('/client/invoices'); - - cy.get('body') - .find('#paid-checkbox') - .check(); - - cy.get('body') - .find('table.invoices-table > tbody > tr') - .first() - .should('not.contain', 'Overdue'); - }); -}); diff --git a/cypress/integration/client_portal/login.spec.js b/cypress/integration/client_portal/login.spec.js deleted file mode 100644 index 3bb0c4e55ec1..000000000000 --- a/cypress/integration/client_portal/login.spec.js +++ /dev/null @@ -1,48 +0,0 @@ -context('Login', () => { - beforeEach(() => { - cy.visit('/client/login'); - }); - - it('should type into login form elements', () => { - cy.get('#test_email') - .invoke('val') - .then(emailValue => { - cy.get('#email') - .type(emailValue) - .should('have.value', emailValue); - }); - - cy.get('#test_password') - .invoke('val') - .then(passwordValue => { - cy.get('#password') - .type(passwordValue) - .should('have.value', passwordValue); - }); - }); - - it('should login into client portal', () => { - cy.get('#test_email') - .invoke('val') - .then(emailValue => { - cy.get('#test_password') - .invoke('val') - .then(passwordValue => { - cy.get('#email') - .type(emailValue) - .should('have.value', emailValue); - cy.get('#password') - .type(passwordValue) - .should('have.value', passwordValue); - cy.get('#loginBtn') - .contains('Login') - .click(); - cy.location().should(location => { - expect(location.pathname).to.eq( - '/client/invoices' - ); - }); - }); - }); - }); -}); diff --git a/cypress/integration/client_portal/payment_methods.spec.js b/cypress/integration/client_portal/payment_methods.spec.js deleted file mode 100644 index 69a682d4ac92..000000000000 --- a/cypress/integration/client_portal/payment_methods.spec.js +++ /dev/null @@ -1,30 +0,0 @@ -context('Payment methods', () => { - beforeEach(() => { - cy.clientLogin(); - }); - - it('should show payment methods page', () => { - cy.visit('/client/payment_methods'); - cy.location().should(location => { - expect(location.pathname).to.eq('/client/payment_methods'); - }); - }); - - it('should show payment methods text', () => { - cy.visit('/client/payment_methods'); - - cy.get('body') - .find('[data-ref=meta-title]') - .first() - .should('contain.text', 'Payment Method'); - }); - - it('should have per page options dropdown', () => { - cy.visit('/client/payment_methods'); - - cy.get('body') - .find('select') - .first() - .should('have.value', '10'); - }); -}); diff --git a/cypress/integration/client_portal/payments.spec.js b/cypress/integration/client_portal/payments.spec.js deleted file mode 100644 index aa735b970999..000000000000 --- a/cypress/integration/client_portal/payments.spec.js +++ /dev/null @@ -1,30 +0,0 @@ -context('Payments', () => { - beforeEach(() => { - cy.clientLogin(); - }); - - it('should show payments page', () => { - cy.visit('/client/payments'); - cy.location().should(location => { - expect(location.pathname).to.eq('/client/payments'); - }); - }); - - it('should show payments text', () => { - cy.visit('/client/payments'); - - cy.get('body') - .find('[data-ref=meta-title]') - .first() - .should('contain.text', 'Payments'); - }); - - it('should have per page options dropdown', () => { - cy.visit('/client/payments'); - - cy.get('body') - .find('select') - .first() - .should('have.value', '10'); - }); -}); diff --git a/cypress/integration/client_portal/quotes.spec.js b/cypress/integration/client_portal/quotes.spec.js deleted file mode 100644 index 538b2fd558eb..000000000000 --- a/cypress/integration/client_portal/quotes.spec.js +++ /dev/null @@ -1,73 +0,0 @@ -describe('Quotes', () => { - beforeEach(() => { - cy.clientLogin(); - }); - - it('should show quotes page', () => { - cy.visit('/client/quotes'); - cy.location().should(location => { - expect(location.pathname).to.eq('/client/quotes'); - }); - }); - - it('should show quotes text', () => { - cy.visit('/client/quotes'); - - cy.get('body') - .find('[data-ref=meta-title]') - .first() - .should('contain.text', 'Quotes'); - }); - - it('should show download and approve buttons', () => { - cy.visit('/client/quotes'); - - cy.get('body') - .find('button[value="download"]') - .first() - .should('contain.text', 'Download'); - - cy.get('body') - .find('button[value="approve"]') - .first() - .should('contain.text', 'Approve'); - }); - - it('should have per page options dropdown', () => { - cy.visit('/client/quotes'); - - cy.get('body') - .find('select') - .first() - .should('have.value', '10'); - }); - - it('should have required table elements', () => { - cy.visit('/client/quotes'); - - cy.get('body') - .find('table.quotes-table > tbody > tr') - .first() - .find('.button-link') - .first() - .should('contain.text', 'View') - .click() - .location() - .should(location => { - expect(location.pathname).to.eq('/client/quotes/VolejRejNm'); - }); - }); - - it('should filter table content', () => { - cy.visit('/client/quotes'); - - cy.get('body') - .find('#draft-checkbox') - .check(); - - cy.get('body') - .find('table.quotes-table > tbody > tr') - .first() - .should('not.contain', 'Sent'); - }); -}); diff --git a/cypress/integration/client_portal/recurring_invoices.spec.js b/cypress/integration/client_portal/recurring_invoices.spec.js deleted file mode 100644 index 7fae096d784a..000000000000 --- a/cypress/integration/client_portal/recurring_invoices.spec.js +++ /dev/null @@ -1,31 +0,0 @@ -context('Recurring invoices', () => { - beforeEach(() => { - cy.clientLogin(); - }); - - it('should show recurring invoices page', () => { - cy.visit('/client/recurring_invoices'); - - cy.location().should(location => { - expect(location.pathname).to.eq('/client/recurring_invoices'); - }); - }); - - it('should show reucrring invoices text', () => { - cy.visit('/client/recurring_invoices'); - - cy.get('body') - .find('[data-ref=meta-title]') - .first() - .should('contain.text', 'Recurring Invoices'); - }); - - it('should have per page options dropdown', () => { - cy.visit('/client/recurring_invoices'); - - cy.get('body') - .find('select') - .first() - .should('have.value', '10'); - }); -}); diff --git a/cypress/integration/examples/actions.spec.js b/cypress/integration/examples/actions.spec.js deleted file mode 100644 index f26ba6343585..000000000000 --- a/cypress/integration/examples/actions.spec.js +++ /dev/null @@ -1,298 +0,0 @@ -/// - -context('Actions', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/actions') - }) - - // https://on.cypress.io/interacting-with-elements - - it('.type() - type into a DOM element', () => { - // https://on.cypress.io/type - cy.get('.action-email') - .type('fake@email.com').should('have.value', 'fake@email.com') - - // .type() with special character sequences - .type('{leftarrow}{rightarrow}{uparrow}{downarrow}') - .type('{del}{selectall}{backspace}') - - // .type() with key modifiers - .type('{alt}{option}') //these are equivalent - .type('{ctrl}{control}') //these are equivalent - .type('{meta}{command}{cmd}') //these are equivalent - .type('{shift}') - - // Delay each keypress by 0.1 sec - .type('slow.typing@email.com', { delay: 100 }) - .should('have.value', 'slow.typing@email.com') - - cy.get('.action-disabled') - // Ignore error checking prior to type - // like whether the input is visible or disabled - .type('disabled error checking', { force: true }) - .should('have.value', 'disabled error checking') - }) - - it('.focus() - focus on a DOM element', () => { - // https://on.cypress.io/focus - cy.get('.action-focus').focus() - .should('have.class', 'focus') - .prev().should('have.attr', 'style', 'color: orange;') - }) - - it('.blur() - blur off a DOM element', () => { - // https://on.cypress.io/blur - cy.get('.action-blur').type('About to blur').blur() - .should('have.class', 'error') - .prev().should('have.attr', 'style', 'color: red;') - }) - - it('.clear() - clears an input or textarea element', () => { - // https://on.cypress.io/clear - cy.get('.action-clear').type('Clear this text') - .should('have.value', 'Clear this text') - .clear() - .should('have.value', '') - }) - - it('.submit() - submit a form', () => { - // https://on.cypress.io/submit - cy.get('.action-form') - .find('[type="text"]').type('HALFOFF') - cy.get('.action-form').submit() - .next().should('contain', 'Your form has been submitted!') - }) - - it('.click() - click on a DOM element', () => { - // https://on.cypress.io/click - cy.get('.action-btn').click() - - // You can click on 9 specific positions of an element: - // ----------------------------------- - // | topLeft top topRight | - // | | - // | | - // | | - // | left center right | - // | | - // | | - // | | - // | bottomLeft bottom bottomRight | - // ----------------------------------- - - // clicking in the center of the element is the default - cy.get('#action-canvas').click() - - cy.get('#action-canvas').click('topLeft') - cy.get('#action-canvas').click('top') - cy.get('#action-canvas').click('topRight') - cy.get('#action-canvas').click('left') - cy.get('#action-canvas').click('right') - cy.get('#action-canvas').click('bottomLeft') - cy.get('#action-canvas').click('bottom') - cy.get('#action-canvas').click('bottomRight') - - // .click() accepts an x and y coordinate - // that controls where the click occurs :) - - cy.get('#action-canvas') - .click(80, 75) // click 80px on x coord and 75px on y coord - .click(170, 75) - .click(80, 165) - .click(100, 185) - .click(125, 190) - .click(150, 185) - .click(170, 165) - - // click multiple elements by passing multiple: true - cy.get('.action-labels>.label').click({ multiple: true }) - - // Ignore error checking prior to clicking - cy.get('.action-opacity>.btn').click({ force: true }) - }) - - it('.dblclick() - double click on a DOM element', () => { - // https://on.cypress.io/dblclick - - // Our app has a listener on 'dblclick' event in our 'scripts.js' - // that hides the div and shows an input on double click - cy.get('.action-div').dblclick().should('not.be.visible') - cy.get('.action-input-hidden').should('be.visible') - }) - - it('.rightclick() - right click on a DOM element', () => { - // https://on.cypress.io/rightclick - - // Our app has a listener on 'contextmenu' event in our 'scripts.js' - // that hides the div and shows an input on right click - cy.get('.rightclick-action-div').rightclick().should('not.be.visible') - cy.get('.rightclick-action-input-hidden').should('be.visible') - }) - - it('.check() - check a checkbox or radio element', () => { - // https://on.cypress.io/check - - // By default, .check() will check all - // matching checkbox or radio elements in succession, one after another - cy.get('.action-checkboxes [type="checkbox"]').not('[disabled]') - .check().should('be.checked') - - cy.get('.action-radios [type="radio"]').not('[disabled]') - .check().should('be.checked') - - // .check() accepts a value argument - cy.get('.action-radios [type="radio"]') - .check('radio1').should('be.checked') - - // .check() accepts an array of values - cy.get('.action-multiple-checkboxes [type="checkbox"]') - .check(['checkbox1', 'checkbox2']).should('be.checked') - - // Ignore error checking prior to checking - cy.get('.action-checkboxes [disabled]') - .check({ force: true }).should('be.checked') - - cy.get('.action-radios [type="radio"]') - .check('radio3', { force: true }).should('be.checked') - }) - - it('.uncheck() - uncheck a checkbox element', () => { - // https://on.cypress.io/uncheck - - // By default, .uncheck() will uncheck all matching - // checkbox elements in succession, one after another - cy.get('.action-check [type="checkbox"]') - .not('[disabled]') - .uncheck().should('not.be.checked') - - // .uncheck() accepts a value argument - cy.get('.action-check [type="checkbox"]') - .check('checkbox1') - .uncheck('checkbox1').should('not.be.checked') - - // .uncheck() accepts an array of values - cy.get('.action-check [type="checkbox"]') - .check(['checkbox1', 'checkbox3']) - .uncheck(['checkbox1', 'checkbox3']).should('not.be.checked') - - // Ignore error checking prior to unchecking - cy.get('.action-check [disabled]') - .uncheck({ force: true }).should('not.be.checked') - }) - - it('.select() - select an option in a element', () => { - // https://on.cypress.io/select - - // at first, no option should be selected - cy.get('.action-select') - .should('have.value', '--Select a fruit--') - - // Select option(s) with matching text content - cy.get('.action-select').select('apples') - // confirm the apples were selected - // note that each value starts with "fr-" in our HTML - cy.get('.action-select').should('have.value', 'fr-apples') - - cy.get('.action-select-multiple') - .select(['apples', 'oranges', 'bananas']) - // when getting multiple values, invoke "val" method first - .invoke('val') - .should('deep.equal', ['fr-apples', 'fr-oranges', 'fr-bananas']) - - // Select option(s) with matching value - cy.get('.action-select').select('fr-bananas') - // can attach an assertion right away to the element - .should('have.value', 'fr-bananas') - - cy.get('.action-select-multiple') - .select(['fr-apples', 'fr-oranges', 'fr-bananas']) - .invoke('val') - .should('deep.equal', ['fr-apples', 'fr-oranges', 'fr-bananas']) - // assert the selected values include oranges - cy.get('.action-select-multiple') - .invoke('val').should('include', 'fr-oranges') - }) - - it('.scrollIntoView() - scroll an element into view', () => { - // https://on.cypress.io/scrollintoview - - // normally all of these buttons are hidden, - // because they're not within - // the viewable area of their parent - // (we need to scroll to see them) - cy.get('#scroll-horizontal button') - .should('not.be.visible') - - // scroll the button into view, as if the user had scrolled - cy.get('#scroll-horizontal button').scrollIntoView() - .should('be.visible') - - cy.get('#scroll-vertical button') - .should('not.be.visible') - - // Cypress handles the scroll direction needed - cy.get('#scroll-vertical button').scrollIntoView() - .should('be.visible') - - cy.get('#scroll-both button') - .should('not.be.visible') - - // Cypress knows to scroll to the right and down - cy.get('#scroll-both button').scrollIntoView() - .should('be.visible') - }) - - it('.trigger() - trigger an event on a DOM element', () => { - // https://on.cypress.io/trigger - - // To interact with a range input (slider) - // we need to set its value & trigger the - // event to signal it changed - - // Here, we invoke jQuery's val() method to set - // the value and trigger the 'change' event - cy.get('.trigger-input-range') - .invoke('val', 25) - .trigger('change') - .get('input[type=range]').siblings('p') - .should('have.text', '25') - }) - - it('cy.scrollTo() - scroll the window or element to a position', () => { - - // https://on.cypress.io/scrollTo - - // You can scroll to 9 specific positions of an element: - // ----------------------------------- - // | topLeft top topRight | - // | | - // | | - // | | - // | left center right | - // | | - // | | - // | | - // | bottomLeft bottom bottomRight | - // ----------------------------------- - - // if you chain .scrollTo() off of cy, we will - // scroll the entire window - cy.scrollTo('bottom') - - cy.get('#scrollable-horizontal').scrollTo('right') - - // or you can scroll to a specific coordinate: - // (x axis, y axis) in pixels - cy.get('#scrollable-vertical').scrollTo(250, 250) - - // or you can scroll to a specific percentage - // of the (width, height) of the element - cy.get('#scrollable-both').scrollTo('75%', '25%') - - // control the easing of the scroll (default is 'swing') - cy.get('#scrollable-vertical').scrollTo('center', { easing: 'linear' }) - - // control the duration of the scroll (in ms) - cy.get('#scrollable-both').scrollTo('center', { duration: 2000 }) - }) -}) diff --git a/cypress/integration/examples/aliasing.spec.js b/cypress/integration/examples/aliasing.spec.js deleted file mode 100644 index c3095745a2aa..000000000000 --- a/cypress/integration/examples/aliasing.spec.js +++ /dev/null @@ -1,42 +0,0 @@ -/// - -context('Aliasing', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/aliasing') - }) - - it('.as() - alias a DOM element for later use', () => { - // https://on.cypress.io/as - - // Alias a DOM element for use later - // We don't have to traverse to the element - // later in our code, we reference it with @ - - cy.get('.as-table').find('tbody>tr') - .first().find('td').first() - .find('button').as('firstBtn') - - // when we reference the alias, we place an - // @ in front of its name - cy.get('@firstBtn').click() - - cy.get('@firstBtn') - .should('have.class', 'btn-success') - .and('contain', 'Changed') - }) - - it('.as() - alias a route for later use', () => { - - // Alias the route to wait for its response - cy.server() - cy.route('GET', 'comments/*').as('getComment') - - // we have code that gets a comment when - // the button is clicked in scripts.js - cy.get('.network-btn').click() - - // https://on.cypress.io/wait - cy.wait('@getComment').its('status').should('eq', 200) - - }) -}) diff --git a/cypress/integration/examples/assertions.spec.js b/cypress/integration/examples/assertions.spec.js deleted file mode 100644 index 81d80acf0071..000000000000 --- a/cypress/integration/examples/assertions.spec.js +++ /dev/null @@ -1,168 +0,0 @@ -/// - -context('Assertions', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/assertions') - }) - - describe('Implicit Assertions', () => { - it('.should() - make an assertion about the current subject', () => { - // https://on.cypress.io/should - cy.get('.assertion-table') - .find('tbody tr:last') - .should('have.class', 'success') - .find('td') - .first() - // checking the text of the element in various ways - .should('have.text', 'Column content') - .should('contain', 'Column content') - .should('have.html', 'Column content') - // chai-jquery uses "is()" to check if element matches selector - .should('match', 'td') - // to match text content against a regular expression - // first need to invoke jQuery method text() - // and then match using regular expression - .invoke('text') - .should('match', /column content/i) - - // a better way to check element's text content against a regular expression - // is to use "cy.contains" - // https://on.cypress.io/contains - cy.get('.assertion-table') - .find('tbody tr:last') - // finds first element with text content matching regular expression - .contains('td', /column content/i) - .should('be.visible') - - // for more information about asserting element's text - // see https://on.cypress.io/using-cypress-faq#How-do-I-get-an-element’s-text-contents - }) - - it('.and() - chain multiple assertions together', () => { - // https://on.cypress.io/and - cy.get('.assertions-link') - .should('have.class', 'active') - .and('have.attr', 'href') - .and('include', 'cypress.io') - }) - }) - - describe('Explicit Assertions', () => { - // https://on.cypress.io/assertions - it('expect - make an assertion about a specified subject', () => { - // We can use Chai's BDD style assertions - expect(true).to.be.true - const o = { foo: 'bar' } - - expect(o).to.equal(o) - expect(o).to.deep.equal({ foo: 'bar' }) - // matching text using regular expression - expect('FooBar').to.match(/bar$/i) - }) - - it('pass your own callback function to should()', () => { - // Pass a function to should that can have any number - // of explicit assertions within it. - // The ".should(cb)" function will be retried - // automatically until it passes all your explicit assertions or times out. - cy.get('.assertions-p') - .find('p') - .should(($p) => { - // https://on.cypress.io/$ - // return an array of texts from all of the p's - // @ts-ignore TS6133 unused variable - const texts = $p.map((i, el) => Cypress.$(el).text()) - - // jquery map returns jquery object - // and .get() convert this to simple array - const paragraphs = texts.get() - - // array should have length of 3 - expect(paragraphs, 'has 3 paragraphs').to.have.length(3) - - // use second argument to expect(...) to provide clear - // message with each assertion - expect(paragraphs, 'has expected text in each paragraph').to.deep.eq([ - 'Some text from first p', - 'More text from second p', - 'And even more text from third p', - ]) - }) - }) - - it('finds element by class name regex', () => { - cy.get('.docs-header') - .find('div') - // .should(cb) callback function will be retried - .should(($div) => { - expect($div).to.have.length(1) - - const className = $div[0].className - - expect(className).to.match(/heading-/) - }) - // .then(cb) callback is not retried, - // it either passes or fails - .then(($div) => { - expect($div, 'text content').to.have.text('Introduction') - }) - }) - - it('can throw any error', () => { - cy.get('.docs-header') - .find('div') - .should(($div) => { - if ($div.length !== 1) { - // you can throw your own errors - throw new Error('Did not find 1 element') - } - - const className = $div[0].className - - if (!className.match(/heading-/)) { - throw new Error(`Could not find class "heading-" in ${className}`) - } - }) - }) - - it('matches unknown text between two elements', () => { - /** - * Text from the first element. - * @type {string} - */ - let text - - /** - * Normalizes passed text, - * useful before comparing text with spaces and different capitalization. - * @param {string} s Text to normalize - */ - const normalizeText = (s) => s.replace(/\s/g, '').toLowerCase() - - cy.get('.two-elements') - .find('.first') - .then(($first) => { - // save text from the first element - text = normalizeText($first.text()) - }) - - cy.get('.two-elements') - .find('.second') - .should(($div) => { - // we can massage text before comparing - const secondText = normalizeText($div.text()) - - expect(secondText, 'second text').to.equal(text) - }) - }) - - it('assert - assert shape of an object', () => { - const person = { - name: 'Joe', - age: 20, - } - - assert.isObject(person, 'value is object') - }) - }) -}) diff --git a/cypress/integration/examples/connectors.spec.js b/cypress/integration/examples/connectors.spec.js deleted file mode 100644 index ae8799181d15..000000000000 --- a/cypress/integration/examples/connectors.spec.js +++ /dev/null @@ -1,97 +0,0 @@ -/// - -context('Connectors', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/connectors') - }) - - it('.each() - iterate over an array of elements', () => { - // https://on.cypress.io/each - cy.get('.connectors-each-ul>li') - .each(($el, index, $list) => { - console.log($el, index, $list) - }) - }) - - it('.its() - get properties on the current subject', () => { - // https://on.cypress.io/its - cy.get('.connectors-its-ul>li') - // calls the 'length' property yielding that value - .its('length') - .should('be.gt', 2) - }) - - it('.invoke() - invoke a function on the current subject', () => { - // our div is hidden in our script.js - // $('.connectors-div').hide() - - // https://on.cypress.io/invoke - cy.get('.connectors-div').should('be.hidden') - // call the jquery method 'show' on the 'div.container' - .invoke('show') - .should('be.visible') - }) - - it('.spread() - spread an array as individual args to callback function', () => { - // https://on.cypress.io/spread - const arr = ['foo', 'bar', 'baz'] - - cy.wrap(arr).spread((foo, bar, baz) => { - expect(foo).to.eq('foo') - expect(bar).to.eq('bar') - expect(baz).to.eq('baz') - }) - }) - - describe('.then()', () => { - it('invokes a callback function with the current subject', () => { - // https://on.cypress.io/then - cy.get('.connectors-list > li') - .then(($lis) => { - expect($lis, '3 items').to.have.length(3) - expect($lis.eq(0), 'first item').to.contain('Walk the dog') - expect($lis.eq(1), 'second item').to.contain('Feed the cat') - expect($lis.eq(2), 'third item').to.contain('Write JavaScript') - }) - }) - - it('yields the returned value to the next command', () => { - cy.wrap(1) - .then((num) => { - expect(num).to.equal(1) - - return 2 - }) - .then((num) => { - expect(num).to.equal(2) - }) - }) - - it('yields the original subject without return', () => { - cy.wrap(1) - .then((num) => { - expect(num).to.equal(1) - // note that nothing is returned from this callback - }) - .then((num) => { - // this callback receives the original unchanged value 1 - expect(num).to.equal(1) - }) - }) - - it('yields the value yielded by the last Cypress command inside', () => { - cy.wrap(1) - .then((num) => { - expect(num).to.equal(1) - // note how we run a Cypress command - // the result yielded by this Cypress command - // will be passed to the second ".then" - cy.wrap(2) - }) - .then((num) => { - // this callback receives the value yielded by "cy.wrap(2)" - expect(num).to.equal(2) - }) - }) - }) -}) diff --git a/cypress/integration/examples/cookies.spec.js b/cypress/integration/examples/cookies.spec.js deleted file mode 100644 index aaf6b5de1e43..000000000000 --- a/cypress/integration/examples/cookies.spec.js +++ /dev/null @@ -1,78 +0,0 @@ -/// - -context('Cookies', () => { - beforeEach(() => { - Cypress.Cookies.debug(true) - - cy.visit('https://example.cypress.io/commands/cookies') - - // clear cookies again after visiting to remove - // any 3rd party cookies picked up such as cloudflare - cy.clearCookies() - }) - - it('cy.getCookie() - get a browser cookie', () => { - // https://on.cypress.io/getcookie - cy.get('#getCookie .set-a-cookie').click() - - // cy.getCookie() yields a cookie object - cy.getCookie('token').should('have.property', 'value', '123ABC') - }) - - it('cy.getCookies() - get browser cookies', () => { - // https://on.cypress.io/getcookies - cy.getCookies().should('be.empty') - - cy.get('#getCookies .set-a-cookie').click() - - // cy.getCookies() yields an array of cookies - cy.getCookies().should('have.length', 1).should((cookies) => { - - // each cookie has these properties - expect(cookies[0]).to.have.property('name', 'token') - expect(cookies[0]).to.have.property('value', '123ABC') - expect(cookies[0]).to.have.property('httpOnly', false) - expect(cookies[0]).to.have.property('secure', false) - expect(cookies[0]).to.have.property('domain') - expect(cookies[0]).to.have.property('path') - }) - }) - - it('cy.setCookie() - set a browser cookie', () => { - // https://on.cypress.io/setcookie - cy.getCookies().should('be.empty') - - cy.setCookie('foo', 'bar') - - // cy.getCookie() yields a cookie object - cy.getCookie('foo').should('have.property', 'value', 'bar') - }) - - it('cy.clearCookie() - clear a browser cookie', () => { - // https://on.cypress.io/clearcookie - cy.getCookie('token').should('be.null') - - cy.get('#clearCookie .set-a-cookie').click() - - cy.getCookie('token').should('have.property', 'value', '123ABC') - - // cy.clearCookies() yields null - cy.clearCookie('token').should('be.null') - - cy.getCookie('token').should('be.null') - }) - - it('cy.clearCookies() - clear browser cookies', () => { - // https://on.cypress.io/clearcookies - cy.getCookies().should('be.empty') - - cy.get('#clearCookies .set-a-cookie').click() - - cy.getCookies().should('have.length', 1) - - // cy.clearCookies() yields null - cy.clearCookies() - - cy.getCookies().should('be.empty') - }) -}) diff --git a/cypress/integration/examples/cypress_api.spec.js b/cypress/integration/examples/cypress_api.spec.js deleted file mode 100644 index 63997697b2d6..000000000000 --- a/cypress/integration/examples/cypress_api.spec.js +++ /dev/null @@ -1,222 +0,0 @@ -/// - -context('Cypress.Commands', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) - - // https://on.cypress.io/custom-commands - - it('.add() - create a custom command', () => { - Cypress.Commands.add('console', { - prevSubject: true, - }, (subject, method) => { - // the previous subject is automatically received - // and the commands arguments are shifted - - // allow us to change the console method used - method = method || 'log' - - // log the subject to the console - // @ts-ignore TS7017 - console[method]('The subject is', subject) - - // whatever we return becomes the new subject - // we don't want to change the subject so - // we return whatever was passed in - return subject - }) - - // @ts-ignore TS2339 - cy.get('button').console('info').then(($button) => { - // subject is still $button - }) - }) -}) - - -context('Cypress.Cookies', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) - - // https://on.cypress.io/cookies - it('.debug() - enable or disable debugging', () => { - Cypress.Cookies.debug(true) - - // Cypress will now log in the console when - // cookies are set or cleared - cy.setCookie('fakeCookie', '123ABC') - cy.clearCookie('fakeCookie') - cy.setCookie('fakeCookie', '123ABC') - cy.clearCookie('fakeCookie') - cy.setCookie('fakeCookie', '123ABC') - }) - - it('.preserveOnce() - preserve cookies by key', () => { - // normally cookies are reset after each test - cy.getCookie('fakeCookie').should('not.be.ok') - - // preserving a cookie will not clear it when - // the next test starts - cy.setCookie('lastCookie', '789XYZ') - Cypress.Cookies.preserveOnce('lastCookie') - }) - - it('.defaults() - set defaults for all cookies', () => { - // now any cookie with the name 'session_id' will - // not be cleared before each new test runs - Cypress.Cookies.defaults({ - whitelist: 'session_id', - }) - }) -}) - - -context('Cypress.Server', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) - - // Permanently override server options for - // all instances of cy.server() - - // https://on.cypress.io/cypress-server - it('.defaults() - change default config of server', () => { - Cypress.Server.defaults({ - delay: 0, - force404: false, - }) - }) -}) - -context('Cypress.arch', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) - - it('Get CPU architecture name of underlying OS', () => { - // https://on.cypress.io/arch - expect(Cypress.arch).to.exist - }) -}) - -context('Cypress.config()', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) - - it('Get and set configuration options', () => { - // https://on.cypress.io/config - let myConfig = Cypress.config() - - expect(myConfig).to.have.property('animationDistanceThreshold', 5) - expect(myConfig).to.have.property('baseUrl', null) - expect(myConfig).to.have.property('defaultCommandTimeout', 4000) - expect(myConfig).to.have.property('requestTimeout', 5000) - expect(myConfig).to.have.property('responseTimeout', 30000) - expect(myConfig).to.have.property('viewportHeight', 660) - expect(myConfig).to.have.property('viewportWidth', 1000) - expect(myConfig).to.have.property('pageLoadTimeout', 60000) - expect(myConfig).to.have.property('waitForAnimations', true) - - expect(Cypress.config('pageLoadTimeout')).to.eq(60000) - - // this will change the config for the rest of your tests! - Cypress.config('pageLoadTimeout', 20000) - - expect(Cypress.config('pageLoadTimeout')).to.eq(20000) - - Cypress.config('pageLoadTimeout', 60000) - }) -}) - -context('Cypress.dom', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) - - // https://on.cypress.io/dom - it('.isHidden() - determine if a DOM element is hidden', () => { - let hiddenP = Cypress.$('.dom-p p.hidden').get(0) - let visibleP = Cypress.$('.dom-p p.visible').get(0) - - // our first paragraph has css class 'hidden' - expect(Cypress.dom.isHidden(hiddenP)).to.be.true - expect(Cypress.dom.isHidden(visibleP)).to.be.false - }) -}) - -context('Cypress.env()', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) - - // We can set environment variables for highly dynamic values - - // https://on.cypress.io/environment-variables - it('Get environment variables', () => { - // https://on.cypress.io/env - // set multiple environment variables - Cypress.env({ - host: 'veronica.dev.local', - api_server: 'http://localhost:8888/v1/', - }) - - // get environment variable - expect(Cypress.env('host')).to.eq('veronica.dev.local') - - // set environment variable - Cypress.env('api_server', 'http://localhost:8888/v2/') - expect(Cypress.env('api_server')).to.eq('http://localhost:8888/v2/') - - // get all environment variable - expect(Cypress.env()).to.have.property('host', 'veronica.dev.local') - expect(Cypress.env()).to.have.property('api_server', 'http://localhost:8888/v2/') - }) -}) - -context('Cypress.log', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) - - it('Control what is printed to the Command Log', () => { - // https://on.cypress.io/cypress-log - }) -}) - - -context('Cypress.platform', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) - - it('Get underlying OS name', () => { - // https://on.cypress.io/platform - expect(Cypress.platform).to.be.exist - }) -}) - -context('Cypress.version', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) - - it('Get current version of Cypress being run', () => { - // https://on.cypress.io/version - expect(Cypress.version).to.be.exist - }) -}) - -context('Cypress.spec', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) - - it('Get current spec information', () => { - // https://on.cypress.io/spec - // wrap the object so we can inspect it easily by clicking in the command log - cy.wrap(Cypress.spec).should('include.keys', ['name', 'relative', 'absolute']) - }) -}) diff --git a/cypress/integration/examples/files.spec.js b/cypress/integration/examples/files.spec.js deleted file mode 100644 index 83c651caba1b..000000000000 --- a/cypress/integration/examples/files.spec.js +++ /dev/null @@ -1,114 +0,0 @@ -/// - -/// JSON fixture file can be loaded directly using -// the built-in JavaScript bundler -// @ts-ignore -const requiredExample = require('../../fixtures/example') - -context('Files', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/files') - }) - - beforeEach(() => { - // load example.json fixture file and store - // in the test context object - cy.fixture('example.json').as('example') - }) - - it('cy.fixture() - load a fixture', () => { - // https://on.cypress.io/fixture - - // Instead of writing a response inline you can - // use a fixture file's content. - - cy.server() - cy.fixture('example.json').as('comment') - // when application makes an Ajax request matching "GET comments/*" - // Cypress will intercept it and reply with object - // from the "comment" alias - cy.route('GET', 'comments/*', '@comment').as('getComment') - - // we have code that gets a comment when - // the button is clicked in scripts.js - cy.get('.fixture-btn').click() - - cy.wait('@getComment').its('responseBody') - .should('have.property', 'name') - .and('include', 'Using fixtures to represent data') - - // you can also just write the fixture in the route - cy.route('GET', 'comments/*', 'fixture:example.json').as('getComment') - - // we have code that gets a comment when - // the button is clicked in scripts.js - cy.get('.fixture-btn').click() - - cy.wait('@getComment').its('responseBody') - .should('have.property', 'name') - .and('include', 'Using fixtures to represent data') - - // or write fx to represent fixture - // by default it assumes it's .json - cy.route('GET', 'comments/*', 'fx:example').as('getComment') - - // we have code that gets a comment when - // the button is clicked in scripts.js - cy.get('.fixture-btn').click() - - cy.wait('@getComment').its('responseBody') - .should('have.property', 'name') - .and('include', 'Using fixtures to represent data') - }) - - it('cy.fixture() or require - load a fixture', function () { - // we are inside the "function () { ... }" - // callback and can use test context object "this" - // "this.example" was loaded in "beforeEach" function callback - expect(this.example, 'fixture in the test context') - .to.deep.equal(requiredExample) - - // or use "cy.wrap" and "should('deep.equal', ...)" assertion - // @ts-ignore - cy.wrap(this.example, 'fixture vs require') - .should('deep.equal', requiredExample) - }) - - it('cy.readFile() - read file contents', () => { - // https://on.cypress.io/readfile - - // You can read a file and yield its contents - // The filePath is relative to your project's root. - cy.readFile('cypress.json').then((json) => { - expect(json).to.be.an('object') - }) - }) - - it('cy.writeFile() - write to a file', () => { - // https://on.cypress.io/writefile - - // You can write to a file - - // Use a response from a request to automatically - // generate a fixture file for use later - cy.request('https://jsonplaceholder.cypress.io/users') - .then((response) => { - cy.writeFile('cypress/fixtures/users.json', response.body) - }) - cy.fixture('users').should((users) => { - expect(users[0].name).to.exist - }) - - // JavaScript arrays and objects are stringified - // and formatted into text. - cy.writeFile('cypress/fixtures/profile.json', { - id: 8739, - name: 'Jane', - email: 'jane@example.com', - }) - - cy.fixture('profile').should((profile) => { - expect(profile.name).to.eq('Jane') - }) - }) -}) diff --git a/cypress/integration/examples/local_storage.spec.js b/cypress/integration/examples/local_storage.spec.js deleted file mode 100644 index 5f83b8de73d7..000000000000 --- a/cypress/integration/examples/local_storage.spec.js +++ /dev/null @@ -1,52 +0,0 @@ -/// - -context('Local Storage', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/local-storage') - }) - // Although local storage is automatically cleared - // in between tests to maintain a clean state - // sometimes we need to clear the local storage manually - - it('cy.clearLocalStorage() - clear all data in local storage', () => { - // https://on.cypress.io/clearlocalstorage - cy.get('.ls-btn').click().should(() => { - expect(localStorage.getItem('prop1')).to.eq('red') - expect(localStorage.getItem('prop2')).to.eq('blue') - expect(localStorage.getItem('prop3')).to.eq('magenta') - }) - - // clearLocalStorage() yields the localStorage object - cy.clearLocalStorage().should((ls) => { - expect(ls.getItem('prop1')).to.be.null - expect(ls.getItem('prop2')).to.be.null - expect(ls.getItem('prop3')).to.be.null - }) - - // Clear key matching string in Local Storage - cy.get('.ls-btn').click().should(() => { - expect(localStorage.getItem('prop1')).to.eq('red') - expect(localStorage.getItem('prop2')).to.eq('blue') - expect(localStorage.getItem('prop3')).to.eq('magenta') - }) - - cy.clearLocalStorage('prop1').should((ls) => { - expect(ls.getItem('prop1')).to.be.null - expect(ls.getItem('prop2')).to.eq('blue') - expect(ls.getItem('prop3')).to.eq('magenta') - }) - - // Clear keys matching regex in Local Storage - cy.get('.ls-btn').click().should(() => { - expect(localStorage.getItem('prop1')).to.eq('red') - expect(localStorage.getItem('prop2')).to.eq('blue') - expect(localStorage.getItem('prop3')).to.eq('magenta') - }) - - cy.clearLocalStorage(/prop1|2/).should((ls) => { - expect(ls.getItem('prop1')).to.be.null - expect(ls.getItem('prop2')).to.be.null - expect(ls.getItem('prop3')).to.eq('magenta') - }) - }) -}) diff --git a/cypress/integration/examples/location.spec.js b/cypress/integration/examples/location.spec.js deleted file mode 100644 index 299867da07ef..000000000000 --- a/cypress/integration/examples/location.spec.js +++ /dev/null @@ -1,32 +0,0 @@ -/// - -context('Location', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/location') - }) - - it('cy.hash() - get the current URL hash', () => { - // https://on.cypress.io/hash - cy.hash().should('be.empty') - }) - - it('cy.location() - get window.location', () => { - // https://on.cypress.io/location - cy.location().should((location) => { - expect(location.hash).to.be.empty - expect(location.href).to.eq('https://example.cypress.io/commands/location') - expect(location.host).to.eq('example.cypress.io') - expect(location.hostname).to.eq('example.cypress.io') - expect(location.origin).to.eq('https://example.cypress.io') - expect(location.pathname).to.eq('/commands/location') - expect(location.port).to.eq('') - expect(location.protocol).to.eq('https:') - expect(location.search).to.be.empty - }) - }) - - it('cy.url() - get the current URL', () => { - // https://on.cypress.io/url - cy.url().should('eq', 'https://example.cypress.io/commands/location') - }) -}) diff --git a/cypress/integration/examples/misc.spec.js b/cypress/integration/examples/misc.spec.js deleted file mode 100644 index c1b962bd0541..000000000000 --- a/cypress/integration/examples/misc.spec.js +++ /dev/null @@ -1,92 +0,0 @@ -/// - -context('Misc', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/misc') - }) - - it('.end() - end the command chain', () => { - // https://on.cypress.io/end - - // cy.end is useful when you want to end a chain of commands - // and force Cypress to re-query from the root element - cy.get('.misc-table').within(() => { - // ends the current chain and yields null - cy.contains('Cheryl').click().end() - - // queries the entire table again - cy.contains('Charles').click() - }) - }) - - it('cy.exec() - execute a system command', () => { - // execute a system command. - // so you can take actions necessary for - // your test outside the scope of Cypress. - // https://on.cypress.io/exec - - // we can use Cypress.platform string to - // select appropriate command - // https://on.cypress/io/platform - cy.log(`Platform ${Cypress.platform} architecture ${Cypress.arch}`) - - // on CircleCI Windows build machines we have a failure to run bash shell - // https://github.com/cypress-io/cypress/issues/5169 - // so skip some of the tests by passing flag "--env circle=true" - const isCircleOnWindows = Cypress.platform === 'win32' && Cypress.env('circle') - - if (isCircleOnWindows) { - return - } - - cy.exec('echo Jane Lane') - .its('stdout').should('contain', 'Jane Lane') - - if (Cypress.platform === 'win32') { - cy.exec('print cypress.json') - .its('stderr').should('be.empty') - } else { - cy.exec('cat cypress.json') - .its('stderr').should('be.empty') - - cy.exec('pwd') - .its('code').should('eq', 0) - } - }) - - it('cy.focused() - get the DOM element that has focus', () => { - // https://on.cypress.io/focused - cy.get('.misc-form').find('#name').click() - cy.focused().should('have.id', 'name') - - cy.get('.misc-form').find('#description').click() - cy.focused().should('have.id', 'description') - }) - - context('Cypress.Screenshot', function () { - it('cy.screenshot() - take a screenshot', () => { - // https://on.cypress.io/screenshot - cy.screenshot('my-image') - }) - - it('Cypress.Screenshot.defaults() - change default config of screenshots', function () { - Cypress.Screenshot.defaults({ - blackout: ['.foo'], - capture: 'viewport', - clip: { x: 0, y: 0, width: 200, height: 200 }, - scale: false, - disableTimersAndAnimations: true, - screenshotOnRunFailure: true, - beforeScreenshot () { }, - afterScreenshot () { }, - }) - }) - }) - - it('cy.wrap() - wrap an object', () => { - // https://on.cypress.io/wrap - cy.wrap({ foo: 'bar' }) - .should('have.property', 'foo') - .and('include', 'bar') - }) -}) diff --git a/cypress/integration/examples/navigation.spec.js b/cypress/integration/examples/navigation.spec.js deleted file mode 100644 index b85a46890c8f..000000000000 --- a/cypress/integration/examples/navigation.spec.js +++ /dev/null @@ -1,56 +0,0 @@ -/// - -context('Navigation', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io') - cy.get('.navbar-nav').contains('Commands').click() - cy.get('.dropdown-menu').contains('Navigation').click() - }) - - it('cy.go() - go back or forward in the browser\'s history', () => { - // https://on.cypress.io/go - - cy.location('pathname').should('include', 'navigation') - - cy.go('back') - cy.location('pathname').should('not.include', 'navigation') - - cy.go('forward') - cy.location('pathname').should('include', 'navigation') - - // clicking back - cy.go(-1) - cy.location('pathname').should('not.include', 'navigation') - - // clicking forward - cy.go(1) - cy.location('pathname').should('include', 'navigation') - }) - - it('cy.reload() - reload the page', () => { - // https://on.cypress.io/reload - cy.reload() - - // reload the page without using the cache - cy.reload(true) - }) - - it('cy.visit() - visit a remote url', () => { - // https://on.cypress.io/visit - - // Visit any sub-domain of your current domain - - // Pass options to the visit - cy.visit('https://example.cypress.io/commands/navigation', { - timeout: 50000, // increase total time for the visit to resolve - onBeforeLoad (contentWindow) { - // contentWindow is the remote page's window object - expect(typeof contentWindow === 'object').to.be.true - }, - onLoad (contentWindow) { - // contentWindow is the remote page's window object - expect(typeof contentWindow === 'object').to.be.true - }, - }) - }) -}) diff --git a/cypress/integration/examples/network_requests.spec.js b/cypress/integration/examples/network_requests.spec.js deleted file mode 100644 index 2c985b8d6e61..000000000000 --- a/cypress/integration/examples/network_requests.spec.js +++ /dev/null @@ -1,195 +0,0 @@ -/// - -context('Network Requests', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/network-requests') - }) - - // Manage AJAX / XHR requests in your app - - it('cy.server() - control behavior of network requests and responses', () => { - // https://on.cypress.io/server - - cy.server().should((server) => { - // the default options on server - // you can override any of these options - expect(server.delay).to.eq(0) - expect(server.method).to.eq('GET') - expect(server.status).to.eq(200) - expect(server.headers).to.be.null - expect(server.response).to.be.null - expect(server.onRequest).to.be.undefined - expect(server.onResponse).to.be.undefined - expect(server.onAbort).to.be.undefined - - // These options control the server behavior - // affecting all requests - - // pass false to disable existing route stubs - expect(server.enable).to.be.true - // forces requests that don't match your routes to 404 - expect(server.force404).to.be.false - // whitelists requests from ever being logged or stubbed - expect(server.whitelist).to.be.a('function') - }) - - cy.server({ - method: 'POST', - delay: 1000, - status: 422, - response: {}, - }) - - // any route commands will now inherit the above options - // from the server. anything we pass specifically - // to route will override the defaults though. - }) - - it('cy.request() - make an XHR request', () => { - // https://on.cypress.io/request - cy.request('https://jsonplaceholder.cypress.io/comments') - .should((response) => { - expect(response.status).to.eq(200) - expect(response.body).to.have.length(500) - expect(response).to.have.property('headers') - expect(response).to.have.property('duration') - }) - }) - - - it('cy.request() - verify response using BDD syntax', () => { - cy.request('https://jsonplaceholder.cypress.io/comments') - .then((response) => { - // https://on.cypress.io/assertions - expect(response).property('status').to.equal(200) - expect(response).property('body').to.have.length(500) - expect(response).to.include.keys('headers', 'duration') - }) - }) - - it('cy.request() with query parameters', () => { - // will execute request - // https://jsonplaceholder.cypress.io/comments?postId=1&id=3 - cy.request({ - url: 'https://jsonplaceholder.cypress.io/comments', - qs: { - postId: 1, - id: 3, - }, - }) - .its('body') - .should('be.an', 'array') - .and('have.length', 1) - .its('0') // yields first element of the array - .should('contain', { - postId: 1, - id: 3, - }) - }) - - it('cy.request() - pass result to the second request', () => { - // first, let's find out the userId of the first user we have - cy.request('https://jsonplaceholder.cypress.io/users?_limit=1') - .its('body') // yields the response object - .its('0') // yields the first element of the returned list - // the above two commands its('body').its('0') - // can be written as its('body.0') - // if you do not care about TypeScript checks - .then((user) => { - expect(user).property('id').to.be.a('number') - // make a new post on behalf of the user - cy.request('POST', 'https://jsonplaceholder.cypress.io/posts', { - userId: user.id, - title: 'Cypress Test Runner', - body: 'Fast, easy and reliable testing for anything that runs in a browser.', - }) - }) - // note that the value here is the returned value of the 2nd request - // which is the new post object - .then((response) => { - expect(response).property('status').to.equal(201) // new entity created - expect(response).property('body').to.contain({ - id: 101, // there are already 100 posts, so new entity gets id 101 - title: 'Cypress Test Runner', - }) - // we don't know the user id here - since it was in above closure - // so in this test just confirm that the property is there - expect(response.body).property('userId').to.be.a('number') - }) - }) - - it('cy.request() - save response in the shared test context', () => { - // https://on.cypress.io/variables-and-aliases - cy.request('https://jsonplaceholder.cypress.io/users?_limit=1') - .its('body').its('0') // yields the first element of the returned list - .as('user') // saves the object in the test context - .then(function () { - // NOTE đŸ‘€ - // By the time this callback runs the "as('user')" command - // has saved the user object in the test context. - // To access the test context we need to use - // the "function () { ... }" callback form, - // otherwise "this" points at a wrong or undefined object! - cy.request('POST', 'https://jsonplaceholder.cypress.io/posts', { - userId: this.user.id, - title: 'Cypress Test Runner', - body: 'Fast, easy and reliable testing for anything that runs in a browser.', - }) - .its('body').as('post') // save the new post from the response - }) - .then(function () { - // When this callback runs, both "cy.request" API commands have finished - // and the test context has "user" and "post" objects set. - // Let's verify them. - expect(this.post, 'post has the right user id').property('userId').to.equal(this.user.id) - }) - }) - - it('cy.route() - route responses to matching requests', () => { - // https://on.cypress.io/route - - let message = 'whoa, this comment does not exist' - - cy.server() - - // Listen to GET to comments/1 - cy.route('GET', 'comments/*').as('getComment') - - // we have code that gets a comment when - // the button is clicked in scripts.js - cy.get('.network-btn').click() - - // https://on.cypress.io/wait - cy.wait('@getComment').its('status').should('eq', 200) - - // Listen to POST to comments - cy.route('POST', '/comments').as('postComment') - - // we have code that posts a comment when - // the button is clicked in scripts.js - cy.get('.network-post').click() - cy.wait('@postComment').should((xhr) => { - expect(xhr.requestBody).to.include('email') - expect(xhr.requestHeaders).to.have.property('Content-Type') - expect(xhr.responseBody).to.have.property('name', 'Using POST in cy.route()') - }) - - // Stub a response to PUT comments/ **** - cy.route({ - method: 'PUT', - url: 'comments/*', - status: 404, - response: { error: message }, - delay: 500, - }).as('putComment') - - // we have code that puts a comment when - // the button is clicked in scripts.js - cy.get('.network-put').click() - - cy.wait('@putComment') - - // our 404 statusCode logic in scripts.js executed - cy.get('.network-put-comment').should('contain', message) - }) -}) diff --git a/cypress/integration/examples/querying.spec.js b/cypress/integration/examples/querying.spec.js deleted file mode 100644 index 00970480f6c8..000000000000 --- a/cypress/integration/examples/querying.spec.js +++ /dev/null @@ -1,114 +0,0 @@ -/// - -context('Querying', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/querying') - }) - - // The most commonly used query is 'cy.get()', you can - // think of this like the '$' in jQuery - - it('cy.get() - query DOM elements', () => { - // https://on.cypress.io/get - - cy.get('#query-btn').should('contain', 'Button') - - cy.get('.query-btn').should('contain', 'Button') - - cy.get('#querying .well>button:first').should('contain', 'Button') - // ↲ - // Use CSS selectors just like jQuery - - cy.get('[data-test-id="test-example"]').should('have.class', 'example') - - // 'cy.get()' yields jQuery object, you can get its attribute - // by invoking `.attr()` method - cy.get('[data-test-id="test-example"]') - .invoke('attr', 'data-test-id') - .should('equal', 'test-example') - - // or you can get element's CSS property - cy.get('[data-test-id="test-example"]') - .invoke('css', 'position') - .should('equal', 'static') - - // or use assertions directly during 'cy.get()' - // https://on.cypress.io/assertions - cy.get('[data-test-id="test-example"]') - .should('have.attr', 'data-test-id', 'test-example') - .and('have.css', 'position', 'static') - }) - - it('cy.contains() - query DOM elements with matching content', () => { - // https://on.cypress.io/contains - cy.get('.query-list') - .contains('bananas') - .should('have.class', 'third') - - // we can pass a regexp to `.contains()` - cy.get('.query-list') - .contains(/^b\w+/) - .should('have.class', 'third') - - cy.get('.query-list') - .contains('apples') - .should('have.class', 'first') - - // passing a selector to contains will - // yield the selector containing the text - cy.get('#querying') - .contains('ul', 'oranges') - .should('have.class', 'query-list') - - cy.get('.query-button') - .contains('Save Form') - .should('have.class', 'btn') - }) - - it('.within() - query DOM elements within a specific element', () => { - // https://on.cypress.io/within - cy.get('.query-form').within(() => { - cy.get('input:first').should('have.attr', 'placeholder', 'Email') - cy.get('input:last').should('have.attr', 'placeholder', 'Password') - }) - }) - - it('cy.root() - query the root DOM element', () => { - // https://on.cypress.io/root - - // By default, root is the document - cy.root().should('match', 'html') - - cy.get('.query-ul').within(() => { - // In this within, the root is now the ul DOM element - cy.root().should('have.class', 'query-ul') - }) - }) - - it('best practices - selecting elements', () => { - // https://on.cypress.io/best-practices#Selecting-Elements - cy.get('[data-cy=best-practices-selecting-elements]').within(() => { - // Worst - too generic, no context - cy.get('button').click() - - // Bad. Coupled to styling. Highly subject to change. - cy.get('.btn.btn-large').click() - - // Average. Coupled to the `name` attribute which has HTML semantics. - cy.get('[name=submission]').click() - - // Better. But still coupled to styling or JS event listeners. - cy.get('#main').click() - - // Slightly better. Uses an ID but also ensures the element - // has an ARIA role attribute - cy.get('#main[role=button]').click() - - // Much better. But still coupled to text content that may change. - cy.contains('Submit').click() - - // Best. Insulated from all changes. - cy.get('[data-cy=submit]').click() - }) - }) -}) diff --git a/cypress/integration/examples/spies_stubs_clocks.spec.js b/cypress/integration/examples/spies_stubs_clocks.spec.js deleted file mode 100644 index e8bdce5ff24b..000000000000 --- a/cypress/integration/examples/spies_stubs_clocks.spec.js +++ /dev/null @@ -1,95 +0,0 @@ -/// - -context('Spies, Stubs, and Clock', () => { - it('cy.spy() - wrap a method in a spy', () => { - // https://on.cypress.io/spy - cy.visit('https://example.cypress.io/commands/spies-stubs-clocks') - - const obj = { - foo () {}, - } - - const spy = cy.spy(obj, 'foo').as('anyArgs') - - obj.foo() - - expect(spy).to.be.called - }) - - it('cy.spy() retries until assertions pass', () => { - cy.visit('https://example.cypress.io/commands/spies-stubs-clocks') - - const obj = { - /** - * Prints the argument passed - * @param x {any} - */ - foo (x) { - console.log('obj.foo called with', x) - }, - } - - cy.spy(obj, 'foo').as('foo') - - setTimeout(() => { - obj.foo('first') - }, 500) - - setTimeout(() => { - obj.foo('second') - }, 2500) - - cy.get('@foo').should('have.been.calledTwice') - }) - - it('cy.stub() - create a stub and/or replace a function with stub', () => { - // https://on.cypress.io/stub - cy.visit('https://example.cypress.io/commands/spies-stubs-clocks') - - const obj = { - /** - * prints both arguments to the console - * @param a {string} - * @param b {string} - */ - foo (a, b) { - console.log('a', a, 'b', b) - }, - } - - const stub = cy.stub(obj, 'foo').as('foo') - - obj.foo('foo', 'bar') - - expect(stub).to.be.called - }) - - it('cy.clock() - control time in the browser', () => { - // https://on.cypress.io/clock - - // create the date in UTC so its always the same - // no matter what local timezone the browser is running in - const now = new Date(Date.UTC(2017, 2, 14)).getTime() - - cy.clock(now) - cy.visit('https://example.cypress.io/commands/spies-stubs-clocks') - cy.get('#clock-div').click() - .should('have.text', '1489449600') - }) - - it('cy.tick() - move time in the browser', () => { - // https://on.cypress.io/tick - - // create the date in UTC so its always the same - // no matter what local timezone the browser is running in - const now = new Date(Date.UTC(2017, 2, 14)).getTime() - - cy.clock(now) - cy.visit('https://example.cypress.io/commands/spies-stubs-clocks') - cy.get('#tick-div').click() - .should('have.text', '1489449600') - cy.tick(10000) // 10 seconds passed - cy.get('#tick-div').click() - .should('have.text', '1489449610') - }) -}) diff --git a/cypress/integration/examples/traversal.spec.js b/cypress/integration/examples/traversal.spec.js deleted file mode 100644 index 0d2cd70a28e2..000000000000 --- a/cypress/integration/examples/traversal.spec.js +++ /dev/null @@ -1,121 +0,0 @@ -/// - -context('Traversal', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/traversal') - }) - - it('.children() - get child DOM elements', () => { - // https://on.cypress.io/children - cy.get('.traversal-breadcrumb') - .children('.active') - .should('contain', 'Data') - }) - - it('.closest() - get closest ancestor DOM element', () => { - // https://on.cypress.io/closest - cy.get('.traversal-badge') - .closest('ul') - .should('have.class', 'list-group') - }) - - it('.eq() - get a DOM element at a specific index', () => { - // https://on.cypress.io/eq - cy.get('.traversal-list>li') - .eq(1).should('contain', 'siamese') - }) - - it('.filter() - get DOM elements that match the selector', () => { - // https://on.cypress.io/filter - cy.get('.traversal-nav>li') - .filter('.active').should('contain', 'About') - }) - - it('.find() - get descendant DOM elements of the selector', () => { - // https://on.cypress.io/find - cy.get('.traversal-pagination') - .find('li').find('a') - .should('have.length', 7) - }) - - it('.first() - get first DOM element', () => { - // https://on.cypress.io/first - cy.get('.traversal-table td') - .first().should('contain', '1') - }) - - it('.last() - get last DOM element', () => { - // https://on.cypress.io/last - cy.get('.traversal-buttons .btn') - .last().should('contain', 'Submit') - }) - - it('.next() - get next sibling DOM element', () => { - // https://on.cypress.io/next - cy.get('.traversal-ul') - .contains('apples').next().should('contain', 'oranges') - }) - - it('.nextAll() - get all next sibling DOM elements', () => { - // https://on.cypress.io/nextall - cy.get('.traversal-next-all') - .contains('oranges') - .nextAll().should('have.length', 3) - }) - - it('.nextUntil() - get next sibling DOM elements until next el', () => { - // https://on.cypress.io/nextuntil - cy.get('#veggies') - .nextUntil('#nuts').should('have.length', 3) - }) - - it('.not() - remove DOM elements from set of DOM elements', () => { - // https://on.cypress.io/not - cy.get('.traversal-disabled .btn') - .not('[disabled]').should('not.contain', 'Disabled') - }) - - it('.parent() - get parent DOM element from DOM elements', () => { - // https://on.cypress.io/parent - cy.get('.traversal-mark') - .parent().should('contain', 'Morbi leo risus') - }) - - it('.parents() - get parent DOM elements from DOM elements', () => { - // https://on.cypress.io/parents - cy.get('.traversal-cite') - .parents().should('match', 'blockquote') - }) - - it('.parentsUntil() - get parent DOM elements from DOM elements until el', () => { - // https://on.cypress.io/parentsuntil - cy.get('.clothes-nav') - .find('.active') - .parentsUntil('.clothes-nav') - .should('have.length', 2) - }) - - it('.prev() - get previous sibling DOM element', () => { - // https://on.cypress.io/prev - cy.get('.birds').find('.active') - .prev().should('contain', 'Lorikeets') - }) - - it('.prevAll() - get all previous sibling DOM elements', () => { - // https://on.cypress.io/prevAll - cy.get('.fruits-list').find('.third') - .prevAll().should('have.length', 2) - }) - - it('.prevUntil() - get all previous sibling DOM elements until el', () => { - // https://on.cypress.io/prevUntil - cy.get('.foods-list').find('#nuts') - .prevUntil('#veggies').should('have.length', 3) - }) - - it('.siblings() - get all sibling DOM elements', () => { - // https://on.cypress.io/siblings - cy.get('.traversal-pills .active') - .siblings().should('have.length', 2) - }) -}) diff --git a/cypress/integration/examples/utilities.spec.js b/cypress/integration/examples/utilities.spec.js deleted file mode 100644 index 753336fcec01..000000000000 --- a/cypress/integration/examples/utilities.spec.js +++ /dev/null @@ -1,133 +0,0 @@ -/// - -context('Utilities', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/utilities') - }) - - it('Cypress._ - call a lodash method', () => { - // https://on.cypress.io/_ - cy.request('https://jsonplaceholder.cypress.io/users') - .then((response) => { - let ids = Cypress._.chain(response.body).map('id').take(3).value() - - expect(ids).to.deep.eq([1, 2, 3]) - }) - }) - - it('Cypress.$ - call a jQuery method', () => { - // https://on.cypress.io/$ - let $li = Cypress.$('.utility-jquery li:first') - - cy.wrap($li) - .should('not.have.class', 'active') - .click() - .should('have.class', 'active') - }) - - it('Cypress.Blob - blob utilities and base64 string conversion', () => { - // https://on.cypress.io/blob - cy.get('.utility-blob').then(($div) => - // https://github.com/nolanlawson/blob-util#imgSrcToDataURL - // get the dataUrl string for the javascript-logo - Cypress.Blob.imgSrcToDataURL('https://example.cypress.io/assets/img/javascript-logo.png', undefined, 'anonymous') - .then((dataUrl) => { - // create an element and set its src to the dataUrl - let img = Cypress.$('', { src: dataUrl }) - - // need to explicitly return cy here since we are initially returning - // the Cypress.Blob.imgSrcToDataURL promise to our test - // append the image - $div.append(img) - - cy.get('.utility-blob img').click() - .should('have.attr', 'src', dataUrl) - })) - }) - - it('Cypress.minimatch - test out glob patterns against strings', () => { - // https://on.cypress.io/minimatch - let matching = Cypress.minimatch('/users/1/comments', '/users/*/comments', { - matchBase: true, - }) - - expect(matching, 'matching wildcard').to.be.true - - matching = Cypress.minimatch('/users/1/comments/2', '/users/*/comments', { - matchBase: true, - }) - expect(matching, 'comments').to.be.false - - // ** matches against all downstream path segments - matching = Cypress.minimatch('/foo/bar/baz/123/quux?a=b&c=2', '/foo/**', { - matchBase: true, - }) - expect(matching, 'comments').to.be.true - - // whereas * matches only the next path segment - - matching = Cypress.minimatch('/foo/bar/baz/123/quux?a=b&c=2', '/foo/*', { - matchBase: false, - }) - expect(matching, 'comments').to.be.false - }) - - - it('Cypress.moment() - format or parse dates using a moment method', () => { - // https://on.cypress.io/moment - const time = Cypress.moment('2014-04-25T19:38:53.196Z').utc().format('h:mm A') - - expect(time).to.be.a('string') - - cy.get('.utility-moment').contains('3:38 PM') - .should('have.class', 'badge') - - // the time in the element should be between 3pm and 5pm - const start = Cypress.moment('3:00 PM', 'LT') - const end = Cypress.moment('5:00 PM', 'LT') - - cy.get('.utility-moment .badge') - .should(($el) => { - // parse American time like "3:38 PM" - const m = Cypress.moment($el.text().trim(), 'LT') - - // display hours + minutes + AM|PM - const f = 'h:mm A' - - expect(m.isBetween(start, end), - `${m.format(f)} should be between ${start.format(f)} and ${end.format(f)}`).to.be.true - }) - }) - - - it('Cypress.Promise - instantiate a bluebird promise', () => { - // https://on.cypress.io/promise - let waited = false - - /** - * @return Bluebird - */ - function waitOneSecond () { - // return a promise that resolves after 1 second - // @ts-ignore TS2351 (new Cypress.Promise) - return new Cypress.Promise((resolve, reject) => { - setTimeout(() => { - // set waited to true - waited = true - - // resolve with 'foo' string - resolve('foo') - }, 1000) - }) - } - - cy.then(() => - // return a promise to cy.then() that - // is awaited until it resolves - // @ts-ignore TS7006 - waitOneSecond().then((str) => { - expect(str).to.eq('foo') - expect(waited).to.be.true - })) - }) -}) diff --git a/cypress/integration/examples/viewport.spec.js b/cypress/integration/examples/viewport.spec.js deleted file mode 100644 index dbcd7eeddd01..000000000000 --- a/cypress/integration/examples/viewport.spec.js +++ /dev/null @@ -1,59 +0,0 @@ -/// - -context('Viewport', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/viewport') - }) - - it('cy.viewport() - set the viewport size and dimension', () => { - // https://on.cypress.io/viewport - - cy.get('#navbar').should('be.visible') - cy.viewport(320, 480) - - // the navbar should have collapse since our screen is smaller - cy.get('#navbar').should('not.be.visible') - cy.get('.navbar-toggle').should('be.visible').click() - cy.get('.nav').find('a').should('be.visible') - - // lets see what our app looks like on a super large screen - cy.viewport(2999, 2999) - - // cy.viewport() accepts a set of preset sizes - // to easily set the screen to a device's width and height - - // We added a cy.wait() between each viewport change so you can see - // the change otherwise it is a little too fast to see :) - - cy.viewport('macbook-15') - cy.wait(200) - cy.viewport('macbook-13') - cy.wait(200) - cy.viewport('macbook-11') - cy.wait(200) - cy.viewport('ipad-2') - cy.wait(200) - cy.viewport('ipad-mini') - cy.wait(200) - cy.viewport('iphone-6+') - cy.wait(200) - cy.viewport('iphone-6') - cy.wait(200) - cy.viewport('iphone-5') - cy.wait(200) - cy.viewport('iphone-4') - cy.wait(200) - cy.viewport('iphone-3') - cy.wait(200) - - // cy.viewport() accepts an orientation for all presets - // the default orientation is 'portrait' - cy.viewport('ipad-2', 'portrait') - cy.wait(200) - cy.viewport('iphone-4', 'landscape') - cy.wait(200) - - // The viewport will be reset back to the default dimensions - // in between tests (the default can be set in cypress.json) - }) -}) diff --git a/cypress/integration/examples/waiting.spec.js b/cypress/integration/examples/waiting.spec.js deleted file mode 100644 index fc5fe1f38ecb..000000000000 --- a/cypress/integration/examples/waiting.spec.js +++ /dev/null @@ -1,34 +0,0 @@ -/// - -context('Waiting', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/waiting') - }) - // BE CAREFUL of adding unnecessary wait times. - // https://on.cypress.io/best-practices#Unnecessary-Waiting - - // https://on.cypress.io/wait - it('cy.wait() - wait for a specific amount of time', () => { - cy.get('.wait-input1').type('Wait 1000ms after typing') - cy.wait(1000) - cy.get('.wait-input2').type('Wait 1000ms after typing') - cy.wait(1000) - cy.get('.wait-input3').type('Wait 1000ms after typing') - cy.wait(1000) - }) - - it('cy.wait() - wait for a specific route', () => { - cy.server() - - // Listen to GET to comments/1 - cy.route('GET', 'comments/*').as('getComment') - - // we have code that gets a comment when - // the button is clicked in scripts.js - cy.get('.network-btn').click() - - // wait for GET comments/1 - cy.wait('@getComment').its('status').should('eq', 200) - }) - -}) diff --git a/cypress/integration/examples/window.spec.js b/cypress/integration/examples/window.spec.js deleted file mode 100644 index f94b64971db1..000000000000 --- a/cypress/integration/examples/window.spec.js +++ /dev/null @@ -1,22 +0,0 @@ -/// - -context('Window', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/window') - }) - - it('cy.window() - get the global window object', () => { - // https://on.cypress.io/window - cy.window().should('have.property', 'top') - }) - - it('cy.document() - get the document object', () => { - // https://on.cypress.io/document - cy.document().should('have.property', 'charset').and('eq', 'UTF-8') - }) - - it('cy.title() - get the title', () => { - // https://on.cypress.io/title - cy.title().should('include', 'Kitchen Sink') - }) -}) diff --git a/cypress/integration/gateways/authorize.net_credit_card.spec.js b/cypress/integration/gateways/authorize.net_credit_card.spec.js deleted file mode 100644 index b7d642656f42..000000000000 --- a/cypress/integration/gateways/authorize.net_credit_card.spec.js +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Invoice Ninja (https://invoiceninja.com) - * - * @link https://github.com/invoiceninja/invoiceninja source repository - * - * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) - * - * @license https://opensource.org/licenses/AAL - */ - -describe('Authorize.net: Credit card test', () => { - before(() => { - cy.artisan('migrate:fresh --seed'); - cy.artisan('ninja:create-single-account authorizenet'); - }); - - beforeEach(() => { - cy.viewport('macbook-13'); - cy.wait(5000); - cy.clientLogin(); - }); - - afterEach(() => { - cy.visit('/client/logout').visit('/client/login'); - }); - - it('should pay with new card', function () { - cy.visit('/client/invoices').then((contentWindow) => { - cy.get('[data-cy=pay-now]').first().click() - .get('[data-cy=pay-now-dropdown]').click() - .get('[data-cy=pay-with-0]').click(); - - cy.get('#card_number').type('4007000000027') - .get('#cardholder_name').type('Invoice Ninja Rocks') - .get('[class=expiry]').type('12/28') - .get('[name=cvc]').type('100'); - - cy.get('#pay-now').click(); - }); - - cy.location('pathname', {timeout: 60000}).should('include', '/client/payments/VolejRejNm'); - }); - - it('should pay with new card & save credit card for future use', function () { - cy.visit('/client/invoices').then((contentWindow) => { - cy.get('[data-cy=pay-now]').first().click() - .get('[data-cy=pay-now-dropdown]').click() - .get('[data-cy=pay-with-0]').click(); - - cy.get('#card_number').type('4007000000027') - .get('#cardholder_name').type('Invoice Ninja Rocks') - .get('[class=expiry]').type('12/28') - .get('[name=cvc]').type('100'); - - cy.get('[name=token-billing-checkbox]').first().check(); - - cy.get('#pay-now').click(); - }); - - cy.location('pathname', {timeout: 60000}).should('include', '/client/payments/Wpmbk5ezJn'); - }); - - it('should pay with saved card (token)', function () { - cy.visit('/client/invoices') - .get('[data-cy=pay-now]').first().click() - .get('[data-cy=pay-now-dropdown]').click() - .get('[data-cy=pay-with-0]').click(); - - cy.get('[name=payment-type]').first().check(); - - cy.get('#pay-now').click(); - - cy.wait(2000); - - cy.location('pathname', {timeout: 60000}).should('include', '/client/payments/Opnel5aKBz'); - }); - - - it('should be able to remove payment method', function () { - cy.visit('/client/payment_methods') - .get('[data-cy=view-payment-method]').click(); - - cy.get('#open-delete-popup').click(); - - cy.get('[data-cy=confirm-payment-removal]').click(); - - cy.url().should('contain', '/client/payment_methods'); - - cy.get('body').contains('Payment method has been successfully removed.'); - }); - - it('should be able to add credit card (standalone)', function () { - cy.visit('/client/payment_methods') - .get('[data-cy=add-payment-method]').click() - .get('[data-cy=add-credit-card-link]').click(); - - cy.get('#card_number').type('4007000000027') - .get('#cardholder_name').type('Invoice Ninja Rocks') - .get('[class=expiry]').type('12/28') - .get('[name=cvc]').type('100'); - - cy.get('#card_button').click(); - - cy.location('pathname', {timeout: 60000}).should('include', '/client/payment_methods'); - }); -}); diff --git a/cypress/integration/gateways/braintree_credit_card.spec.js b/cypress/integration/gateways/braintree_credit_card.spec.js deleted file mode 100644 index 967041045034..000000000000 --- a/cypress/integration/gateways/braintree_credit_card.spec.js +++ /dev/null @@ -1,75 +0,0 @@ -context('Checkout.com: Credit card testing', () => { - beforeEach(() => { - cy.clientLogin(); - }); - - afterEach(() => { - cy.visit('/client/logout'); - }); - - it('should not be able to add payment method', function () { - cy.visit('/client/payment_methods'); - - cy.get('[data-cy=add-payment-method]').click(); - cy.get('[data-cy=add-credit-card-link]').click(); - - cy.get('[data-ref=gateway-container]') - .contains('This payment method can be can saved for future use, once you complete your first transaction. Don\'t forget to check "Store credit card details" during payment process.'); - }); - - it('should pay with new card', function () { - cy.visit('/client/invoices'); - - cy.get('[data-cy=pay-now]').first().click(); - cy.get('[data-cy=pay-now-dropdown]').click(); - cy.get('[data-cy=pay-with-0]').click(); - - cy - .get('#braintree-hosted-field-number') - .wait(5000) - .iframeLoaded() - .its('document') - .getInDocument('#credit-card-number') - .type(4111111111111111) - - cy - .get('#braintree-hosted-field-expirationDate') - .wait(5000) - .iframeLoaded() - .its('document') - .getInDocument('#expiration') - .type(1224) - - cy.get('#pay-now').click(); - - cy.url().should('contain', '/client/payments/VolejRejNm'); - }); - - it('should pay with saved card (token)', function () { - cy.visit('/client/invoices'); - - cy.get('[data-cy=pay-now]').first().click(); - cy.get('[data-cy=pay-now-dropdown]').click(); - cy.get('[data-cy=pay-with-0]').click(); - - cy.get('[name=payment-type]').first().check(); - - cy.get('#pay-now-with-token').click(); - - cy.url().should('contain', '/client/payments/Opnel5aKBz'); - }); - - it('should be able to remove payment method', function () { - cy.visit('/client/payment_methods'); - - cy.get('[data-cy=view-payment-method]').click(); - - cy.get('#open-delete-popup').click(); - - cy.get('[data-cy=confirm-payment-removal]').click(); - - cy.url().should('contain', '/client/payment_methods'); - - cy.get('body').contains('Payment method has been successfully removed.'); - }); -}); diff --git a/cypress/integration/gateways/checkout_credit_card.spec.js b/cypress/integration/gateways/checkout_credit_card.spec.js deleted file mode 100644 index b2ce01811133..000000000000 --- a/cypress/integration/gateways/checkout_credit_card.spec.js +++ /dev/null @@ -1,87 +0,0 @@ -context('Checkout.com: Credit card testing', () => { - before(() => { - cy.artisan('migrate:fresh --seed'); - cy.artisan('ninja:create-single-account checkout'); - }); - - beforeEach(() => { - cy.viewport('macbook-13'); - cy.clientLogin(); - }); - - afterEach(() => { - cy.visit('/client/logout'); - }); - - it('should not be able to add payment method', function () { - cy.visit('/client/payment_methods'); - - cy.get('[data-cy=add-payment-method]').click(); - cy.get('[data-cy=add-credit-card-link]').click(); - - cy.get('[data-ref=gateway-container]') - .contains('Checkout.com can be can saved as payment method for future use, once you complete your first transaction. Don\'t forget to check "Store credit card details" during payment process.'); - }); - - it('should pay with new card', function () { - cy.visit('/client/invoices'); - - cy.get('[data-cy=pay-now]').first().click(); - cy.get('[data-cy=pay-now-dropdown]').click(); - cy.get('[data-cy=pay-with-0]').click(); - - cy.getWithinIframe('#checkout-frames-card-number').type('4658584090000001'); - cy.getWithinIframe('#checkout-frames-expiry-date').type('12/22'); - cy.getWithinIframe('#checkout-frames-cvv').type('257'); - - cy.get('#pay-button').click(); - - cy.url().should('contain', '/client/payments/VolejRejNm'); - }); - - it('should pay with new card & save credit card for future use', function () { - cy.visit('/client/invoices'); - - cy.get('[data-cy=pay-now]').first().click(); - cy.get('[data-cy=pay-now-dropdown]').click(); - cy.get('[data-cy=pay-with-0]').click(); - - cy.get('[name=token-billing-checkbox]').first().check(); - - cy.getWithinIframe('#checkout-frames-card-number').type('4543474002249996'); - cy.getWithinIframe('#checkout-frames-expiry-date').type('12/22'); - cy.getWithinIframe('#checkout-frames-cvv').type('956'); - - cy.get('#pay-button').click(); - - cy.url().should('contain', '/client/payments/Wpmbk5ezJn'); - }); - - it('should pay with saved card (token)', function () { - cy.visit('/client/invoices'); - - cy.get('[data-cy=pay-now]').first().click(); - cy.get('[data-cy=pay-now-dropdown]').click(); - cy.get('[data-cy=pay-with-0]').click(); - - cy.get('[name=payment-type]').first().check(); - - cy.get('#pay-now-with-token').click(); - - cy.url().should('contain', '/client/payments/Opnel5aKBz'); - }); - - it('should be able to remove payment method', function () { - cy.visit('/client/payment_methods'); - - cy.get('[data-cy=view-payment-method]').click(); - - cy.get('#open-delete-popup').click(); - - cy.get('[data-cy=confirm-payment-removal]').click(); - - cy.url().should('contain', '/client/payment_methods'); - - cy.get('body').contains('Payment method has been successfully removed.'); - }); -}); diff --git a/cypress/integration/gateways/stripe_ach.spec.js b/cypress/integration/gateways/stripe_ach.spec.js deleted file mode 100644 index dc50e0450ffa..000000000000 --- a/cypress/integration/gateways/stripe_ach.spec.js +++ /dev/null @@ -1,248 +0,0 @@ -import axios from 'axios'; - -describe('Stripe: ACH testing', () => { - before(() => { - cy.artisan('migrate:fresh --seed'); - cy.artisan('ninja:create-single-account stripe'); - }); - - beforeEach(() => { - let headers = { - 'X-API-Token': 'company-token-test', - 'X-Requested-With': 'XMLHttpRequest', - 'Content-Type': 'application/json; charset=utf-8', - }; - - let gatewaysBody = { - "gateway_key": "d14dd26a37cecc30fdd65700bfb55b23", - "accepted_credit_cards": 0, - "require_shipping_address": true, - "require_billing_address": true, - "require_client_name": false, - "require_client_phone": false, - "require_contact_name": false, - "require_contact_email": false, - "require_cvv": true, - "update_details": true, - "fees_and_limits": { - "1": { - "min_limit": -1, - "max_limit": -1, - "fee_amount": 0, - "fee_percent": 0, - "fee_cap": 0, - "fee_tax_rate1": 0, - "fee_tax_name1": "", - "fee_tax_rate2": 0, - "fee_tax_name2": "", - "fee_tax_rate3": 0, - "fee_tax_name3": "", - "adjust_fee_percent": false, - "is_enabled": true - }, - "2": { - "min_limit": -1, - "max_limit": -1, - "fee_amount": 0, - "fee_percent": 0, - "fee_cap": 0, - "fee_tax_rate1": 0, - "fee_tax_name1": "", - "fee_tax_rate2": 0, - "fee_tax_name2": "", - "fee_tax_rate3": 0, - "fee_tax_name3": "", - "adjust_fee_percent": false, - "is_enabled": true - }, - "6": { - "min_limit": -1, - "max_limit": -1, - "fee_amount": 0, - "fee_percent": 0, - "fee_cap": 0, - "fee_tax_rate1": 0, - "fee_tax_name1": "", - "fee_tax_rate2": 0, - "fee_tax_name2": "", - "fee_tax_rate3": 0, - "fee_tax_name3": "", - "adjust_fee_percent": false, - "is_enabled": true - } - }, - "system_logs": [], - "custom_value1": "", - "custom_value2": "", - "custom_value3": "", - "custom_value4": "", - "config": "{\"apiKey\":\"sk_test_Yorqvz45sZWSSUmvCfoKF8e6\",\"publishableKey\":\"pk_test_P1riKDKD0pdNTkHwBWEZ8DR0\",\"enable_ach\":\"1\",\"enable_sofort\":\"1\",\"enable_apple_pay\":\"0\",\"enable_alipay\":\"0\"}", - "token_billing": "off", - "test_mode": true, - "label": "Stripe", - "created_at": 1612791181, - "updated_at": 1612792176, - "archived_at": 0, - "id": "VolejRejNm", - "loadedAt": 1612792176934, - "require_postal_code": false, - "is_deleted": false - }; - let clientBody = { - "group_settings_id": "", - "name": "Batz LLC", - "display_name": "Batz LLC", - "balance": 8323.7, - "credit_balance": 0, - "paid_to_date": 0, - "client_hash": "DxrMypcMdnYJvfebfeoXUi2Iyear6LkNq7Twi0H9", - "address1": "45804", - "address2": "47988 Rex Mall", - "city": "New Macberg", - "state": "Florida", - "postal_code": "43089-5809", - "country_id": "840", - "phone": "", - "private_notes": "Neque libero eos adipisci quae. Non voluptas quaerat ea nisi repudiandae in. Voluptatem error aut est distinctio perspiciatis quis.", - "public_notes": "", - "website": "https://www.wintheiser.com/non-velit-nisi-culpa-sit-optio-omnis-ipsum-pariatur", - "industry_id": "", - "size_id": "", - "vat_number": "157764830", - "id_number": "", - "number": "0001", - "shipping_address1": "5181", - "shipping_address2": "66797 Jedediah Isle Suite 479", - "shipping_city": "Lake Rosariomouth", - "shipping_state": "Nevada", - "shipping_postal_code": "31693", - "shipping_country_id": "4", - "settings": { - "currency_id": "1" - }, - "last_login": 0, - "custom_value1": "", - "custom_value2": "", - "custom_value3": "", - "custom_value4": "", - "contacts": [ - { - "first_name": "Rita", - "last_name": "Pouros", - "email": "user@example.com", - "password": "**********", - "phone": "+1-331-663-8498", - "contact_key": "hNQkBU6RM6tG2pwu4J7dCfuq2ZdH6Q8anEvKnyoL", - "is_primary": true, - "send_email": true, - "custom_value1": "", - "custom_value2": "", - "custom_value3": "", - "custom_value4": "", - "last_login": 0, - "link": "https://localhost:8080/client/key_login/hNQkBU6RM6tG2pwu4J7dCfuq2ZdH6Q8anEvKnyoL", - "created_at": 1612792539, - "updated_at": 1612792539, - "archived_at": 0, - "id": "VolejRejNm" - }, - { - "first_name": "Danika", - "last_name": "Hauck", - "email": "bbrakus@example.net", - "password": "**********", - "phone": "662-968-5275 x48146", - "contact_key": "4hWqvVUv2bwYIOb25rWmQhbhadnl5yneTzglGZ32", - "is_primary": false, - "send_email": true, - "custom_value1": "", - "custom_value2": "", - "custom_value3": "", - "custom_value4": "", - "last_login": 0, - "link": "https://localhost:8080/client/key_login/4hWqvVUv2bwYIOb25rWmQhbhadnl5yneTzglGZ32", - "created_at": 1612792539, - "updated_at": 1612792539, - "archived_at": 0, - "id": "Wpmbk5ezJn" - } - ], - "activities": [], - "ledger": [], - "gateway_tokens": [], - "documents": [], - "system_logs": [], - "created_at": 1612792539, - "updated_at": 1612792565, - "archived_at": 0, - "id": "VolejRejNm", - "isChanged": true, - "is_deleted": false, - "user_id": "VolejRejNm", - "assigned_user_id": "" - }; - - axios.put('https://localhost:8080/api/v1/company_gateways/VolejRejNm', gatewaysBody, {headers}) - axios.put('https://localhost:8080/api/v1/clients/VolejRejNm', clientBody, {headers}); // Set country to US. - - cy.viewport('macbook-13'); - cy.clientLogin(); - }); - - afterEach(() => { - cy.visit('/client/logout').visit('/client/login'); - }); - - it('should be able to add bank account & verify it', function () { - cy.visit('/client/payment_methods'); - - cy.get('[data-cy=add-payment-method]').click(); - cy.get('[data-cy=add-bank-account-link]').click(); - - cy.get('#account-holder-name').type('Invoice Ninja Rocks'); - cy.get('#country').select('US'); - cy.get('#currency').select('USD'); - cy.get('#routing-number').type('110000000'); - cy.get('#account-number').type('000123456789'); - cy.get('#accept-terms').check(); - - cy.get('#save-button').click(); - - cy.url().should('contain', 'method=2'); - - cy.get('[data-cy=verification-1st]').type('32'); - cy.get('[data-cy=verification-2nd]').type('45'); - - cy.get('#pay-now').click(); - - cy.get('body').contains('Verification completed successfully'); - }); - - it('should be able to pay the invoice', function () { - cy.visit('/client/invoices'); - - cy.get('[data-cy=pay-now]').first().click(); - cy.get('[data-cy=pay-now-dropdown]').click(); - cy.get('[data-cy=pay-with-2]').click(); - - cy.get('[name=payment-type]').first().check(); - - cy.get('#pay-now').click(); - - cy.url().should('contain', '/client/payments/'); - }); - - it('should be able to remove payment method', function () { - cy.visit('/client/payment_methods'); - - cy.get('[data-cy=view-payment-method]').click(); - - cy.get('#open-delete-popup').click(); - - cy.get('[data-cy=confirm-payment-removal]').click(); - - cy.url().should('contain', '/client/payment_methods'); - - cy.get('body').contains('Payment method has been successfully removed.'); - }); -}); diff --git a/cypress/integration/gateways/stripe_alipay.spec.js b/cypress/integration/gateways/stripe_alipay.spec.js deleted file mode 100644 index 16e0d2653ec4..000000000000 --- a/cypress/integration/gateways/stripe_alipay.spec.js +++ /dev/null @@ -1,209 +0,0 @@ -import axios from "axios"; - -describe('Stripe: Alipay testing', () => { - before(() => { - cy.artisan('migrate:fresh --seed'); - cy.artisan('ninja:create-single-account checkout'); - }); - - beforeEach(() => { - let headers = { - 'X-API-Token': 'company-token-test', - 'X-Requested-With': 'XMLHttpRequest', - 'Content-Type': 'application/json; charset=utf-8', - }; - - let gatewaysBody = { - "gateway_key": "d14dd26a37cecc30fdd65700bfb55b23", - "accepted_credit_cards": 0, - "require_shipping_address": true, - "require_billing_address": true, - "require_client_name": false, - "require_client_phone": false, - "require_contact_name": false, - "require_contact_email": false, - "require_cvv": true, - "update_details": true, - "fees_and_limits": { - "1": { - "min_limit": -1, - "max_limit": -1, - "fee_amount": 0, - "fee_percent": 0, - "fee_cap": 0, - "fee_tax_rate1": 0, - "fee_tax_name1": "", - "fee_tax_rate2": 0, - "fee_tax_name2": "", - "fee_tax_rate3": 0, - "fee_tax_name3": "", - "adjust_fee_percent": false, - "is_enabled": true - }, - "2": { - "min_limit": -1, - "max_limit": -1, - "fee_amount": 0, - "fee_percent": 0, - "fee_cap": 0, - "fee_tax_rate1": 0, - "fee_tax_name1": "", - "fee_tax_rate2": 0, - "fee_tax_name2": "", - "fee_tax_rate3": 0, - "fee_tax_name3": "", - "adjust_fee_percent": false, - "is_enabled": true - }, - "6": { - "min_limit": -1, - "max_limit": -1, - "fee_amount": 0, - "fee_percent": 0, - "fee_cap": 0, - "fee_tax_rate1": 0, - "fee_tax_name1": "", - "fee_tax_rate2": 0, - "fee_tax_name2": "", - "fee_tax_rate3": 0, - "fee_tax_name3": "", - "adjust_fee_percent": false, - "is_enabled": true - } - }, - "system_logs": [], - "custom_value1": "", - "custom_value2": "", - "custom_value3": "", - "custom_value4": "", - "config": "{\"apiKey\":\"sk_test_Yorqvz45sZWSSUmvCfoKF8e6\",\"publishableKey\":\"pk_test_P1riKDKD0pdNTkHwBWEZ8DR0\",\"enable_ach\":\"0\",\"enable_sofort\":\"0\",\"enable_apple_pay\":\"0\",\"enable_alipay\":\"1\"}", - "token_billing": "off", - "test_mode": true, - "label": "Stripe", - "created_at": 1612791181, - "updated_at": 1612792176, - "archived_at": 0, - "id": "VolejRejNm", - "loadedAt": 1612792176934, - "require_postal_code": false, - "is_deleted": false - }; - let clientBody = { - "group_settings_id": "", - "name": "Batz LLC", - "display_name": "Batz LLC", - "balance": 8323.7, - "credit_balance": 0, - "paid_to_date": 0, - "client_hash": "DxrMypcMdnYJvfebfeoXUi2Iyear6LkNq7Twi0H9", - "address1": "45804", - "address2": "47988 Rex Mall", - "city": "New Macberg", - "state": "Florida", - "postal_code": "43089-5809", - "country_id": "840", - "phone": "", - "private_notes": "Neque libero eos adipisci quae. Non voluptas quaerat ea nisi repudiandae in. Voluptatem error aut est distinctio perspiciatis quis.", - "public_notes": "", - "website": "https://www.wintheiser.com/non-velit-nisi-culpa-sit-optio-omnis-ipsum-pariatur", - "industry_id": "", - "size_id": "", - "vat_number": "157764830", - "id_number": "", - "number": "0001", - "shipping_address1": "5181", - "shipping_address2": "66797 Jedediah Isle Suite 479", - "shipping_city": "Lake Rosariomouth", - "shipping_state": "Nevada", - "shipping_postal_code": "31693", - "shipping_country_id": "4", - "settings": { - "currency_id": "1" - }, - "last_login": 0, - "custom_value1": "", - "custom_value2": "", - "custom_value3": "", - "custom_value4": "", - "contacts": [ - { - "first_name": "Rita", - "last_name": "Pouros", - "email": "user@example.com", - "password": "**********", - "phone": "+1-331-663-8498", - "contact_key": "hNQkBU6RM6tG2pwu4J7dCfuq2ZdH6Q8anEvKnyoL", - "is_primary": true, - "send_email": true, - "custom_value1": "", - "custom_value2": "", - "custom_value3": "", - "custom_value4": "", - "last_login": 0, - "link": "https://localhost:8080/client/key_login/hNQkBU6RM6tG2pwu4J7dCfuq2ZdH6Q8anEvKnyoL", - "created_at": 1612792539, - "updated_at": 1612792539, - "archived_at": 0, - "id": "VolejRejNm" - }, - { - "first_name": "Danika", - "last_name": "Hauck", - "email": "bbrakus@example.net", - "password": "**********", - "phone": "662-968-5275 x48146", - "contact_key": "4hWqvVUv2bwYIOb25rWmQhbhadnl5yneTzglGZ32", - "is_primary": false, - "send_email": true, - "custom_value1": "", - "custom_value2": "", - "custom_value3": "", - "custom_value4": "", - "last_login": 0, - "link": "https://localhost:8080/client/key_login/4hWqvVUv2bwYIOb25rWmQhbhadnl5yneTzglGZ32", - "created_at": 1612792539, - "updated_at": 1612792539, - "archived_at": 0, - "id": "Wpmbk5ezJn" - } - ], - "activities": [], - "ledger": [], - "gateway_tokens": [], - "documents": [], - "system_logs": [], - "created_at": 1612792539, - "updated_at": 1612792565, - "archived_at": 0, - "id": "VolejRejNm", - "isChanged": true, - "is_deleted": false, - "user_id": "VolejRejNm", - "assigned_user_id": "" - }; - - axios.put('https://localhost:8080/api/v1/company_gateways/VolejRejNm', gatewaysBody, {headers}) - axios.put('https://localhost:8080/api/v1/clients/VolejRejNm', clientBody, {headers}); // Set country to US. - - cy.viewport('macbook-13'); - cy.clientLogin(); - }); - - afterEach(() => { - cy.visit('/client/logout').visit('/client/login'); - }); - - it('should be able to pay using Alipay', function () { - cy.visit('/client/invoices'); - - cy.get('[data-cy=pay-now]').first().click(); - cy.get('[data-cy=pay-now-dropdown]').click(); - cy.get('[data-cy=pay-with-2]').click(); - - cy.get('#pay-now').click(); - - cy.get('.common-ButtonGroup > .common-Button--default').click(); - - cy.url().should('contain', '/client/payments/'); - }); -}); diff --git a/cypress/integration/gateways/stripe_credit_card.spec.js b/cypress/integration/gateways/stripe_credit_card.spec.js deleted file mode 100644 index ff206248fdc0..000000000000 --- a/cypress/integration/gateways/stripe_credit_card.spec.js +++ /dev/null @@ -1,95 +0,0 @@ -describe('Stripe: Credit card testing', () => { - before(() => { - cy.artisan('migrate:fresh --seed'); - cy.artisan('ninja:create-single-account stripe'); - }); - - beforeEach(() => { - cy.viewport('macbook-13'); - cy.clientLogin(); - }); - - afterEach(() => { - cy.visit('/client/logout').visit('/client/login'); - }); - - it('should pay with new card', function () { - cy.visit('/client/invoices') - .get('[data-cy=pay-now]').first().click() - .get('[data-cy=pay-now-dropdown]').click() - .get('[data-cy=pay-with-0]').click(); - - cy.get('#cardholder-name').type('Invoice Ninja Rocks'); - cy.getWithinIframe('[name=cardnumber]').type('4242424242424242'); - cy.getWithinIframe('[name=exp-date]').type('04/24'); - cy.getWithinIframe('[name=cvc]').type('242'); - cy.getWithinIframe('[name=postal]').type('42424'); - - cy.get('#pay-now').click(); - - cy.location('pathname', {timeout: 60000}).should('include', '/client/payments/VolejRejNm'); - }); - - it('should pay with new card & save credit card for future use', function () { - cy.visit('/client/invoices') - .get('[data-cy=pay-now]').first().click() - .get('[data-cy=pay-now-dropdown]').click() - .get('[data-cy=pay-with-0]').click(); - - cy.get('#cardholder-name').type('Invoice Ninja Rocks'); - cy.getWithinIframe('[name=cardnumber]').type('4242424242424242'); - cy.getWithinIframe('[name=exp-date]').type('04/24'); - cy.getWithinIframe('[name=cvc]').type('242'); - cy.getWithinIframe('[name=postal]').type('42424'); - - cy.get('[name=token-billing-checkbox]').first().check(); - - cy.get('#pay-now').click(); - - cy.location('pathname', {timeout: 60000}).should('include', '/client/payments/Wpmbk5ezJn'); - }); - - it('should pay with saved card (token)', function () { - cy.visit('/client/invoices') - .get('[data-cy=pay-now]').first().click() - .get('[data-cy=pay-now-dropdown]').click() - .get('[data-cy=pay-with-0]').click(); - - cy.get('[name=payment-type]').first().check(); - - cy.get('#pay-now').click(); - - cy.location('pathname', {timeout: 60000}).should('include', '/client/payments/Opnel5aKBz'); - }); - - it('should be able to remove payment method', function () { - cy.visit('/client/payment_methods') - .get('[data-cy=view-payment-method]').click(); - - cy.get('#open-delete-popup').click(); - - cy.get('[data-cy=confirm-payment-removal]').click(); - - cy.url().should('contain', '/client/payment_methods'); - - cy.get('body').contains('Payment method has been successfully removed.'); - }); - - it('should be able to add credit card (standalone)', function () { - cy.visit('/client/payment_methods') - .get('[data-cy=add-payment-method]').click() - .get('[data-cy=add-credit-card-link]').click(); - - cy.get('#cardholder-name').type('Invoice Ninja Rocks'); - cy.getWithinIframe('[name=cardnumber]').type('4242424242424242'); - cy.getWithinIframe('[name=exp-date]').type('04/24'); - cy.getWithinIframe('[name=cvc]').type('242'); - cy.getWithinIframe('[name=postal]').type('42424'); - - cy.get('#authorize-card').click(); - - cy.location('pathname', {timeout: 60000}) - .should('include', '/client/payment_methods') - .get('[data-cy=pm-last4]').contains('**** 4242'); - }); -}); diff --git a/cypress/integration/gateways/stripe_sofort.spec.js b/cypress/integration/gateways/stripe_sofort.spec.js deleted file mode 100644 index 38073166b97f..000000000000 --- a/cypress/integration/gateways/stripe_sofort.spec.js +++ /dev/null @@ -1,209 +0,0 @@ -import axios from "axios"; - -describe('Stripe: SOFORT testing', () => { - before(() => { - cy.artisan('migrate:fresh --seed'); - cy.artisan('ninja:create-single-account checkout'); - }); - - beforeEach(() => { - let headers = { - 'X-API-Token': 'company-token-test', - 'X-Requested-With': 'XMLHttpRequest', - 'Content-Type': 'application/json; charset=utf-8', - }; - - let gatewaysBody = { - "gateway_key": "d14dd26a37cecc30fdd65700bfb55b23", - "accepted_credit_cards": 0, - "require_shipping_address": true, - "require_billing_address": true, - "require_client_name": false, - "require_client_phone": false, - "require_contact_name": false, - "require_contact_email": false, - "require_cvv": true, - "update_details": true, - "fees_and_limits": { - "1": { - "min_limit": -1, - "max_limit": -1, - "fee_amount": 0, - "fee_percent": 0, - "fee_cap": 0, - "fee_tax_rate1": 0, - "fee_tax_name1": "", - "fee_tax_rate2": 0, - "fee_tax_name2": "", - "fee_tax_rate3": 0, - "fee_tax_name3": "", - "adjust_fee_percent": false, - "is_enabled": true - }, - "2": { - "min_limit": -1, - "max_limit": -1, - "fee_amount": 0, - "fee_percent": 0, - "fee_cap": 0, - "fee_tax_rate1": 0, - "fee_tax_name1": "", - "fee_tax_rate2": 0, - "fee_tax_name2": "", - "fee_tax_rate3": 0, - "fee_tax_name3": "", - "adjust_fee_percent": false, - "is_enabled": true - }, - "6": { - "min_limit": -1, - "max_limit": -1, - "fee_amount": 0, - "fee_percent": 0, - "fee_cap": 0, - "fee_tax_rate1": 0, - "fee_tax_name1": "", - "fee_tax_rate2": 0, - "fee_tax_name2": "", - "fee_tax_rate3": 0, - "fee_tax_name3": "", - "adjust_fee_percent": false, - "is_enabled": true - } - }, - "system_logs": [], - "custom_value1": "", - "custom_value2": "", - "custom_value3": "", - "custom_value4": "", - "config": "{\"apiKey\":\"sk_test_Yorqvz45sZWSSUmvCfoKF8e6\",\"publishableKey\":\"pk_test_P1riKDKD0pdNTkHwBWEZ8DR0\",\"enable_ach\":\"1\",\"enable_sofort\":\"1\",\"enable_apple_pay\":\"0\",\"enable_alipay\":\"0\"}", - "token_billing": "off", - "test_mode": true, - "label": "Stripe", - "created_at": 1612791181, - "updated_at": 1612792176, - "archived_at": 0, - "id": "VolejRejNm", - "loadedAt": 1612792176934, - "require_postal_code": false, - "is_deleted": false - }; - let clientBody = { - "group_settings_id": "", - "name": "Batz LLC", - "display_name": "Batz LLC", - "balance": 8323.7, - "credit_balance": 0, - "paid_to_date": 0, - "client_hash": "DxrMypcMdnYJvfebfeoXUi2Iyear6LkNq7Twi0H9", - "address1": "45804", - "address2": "47988 Rex Mall", - "city": "New Macberg", - "state": "Florida", - "postal_code": "43089-5809", - "country_id": "276", - "phone": "", - "private_notes": "Neque libero eos adipisci quae. Non voluptas quaerat ea nisi repudiandae in. Voluptatem error aut est distinctio perspiciatis quis.", - "public_notes": "", - "website": "https://www.wintheiser.com/non-velit-nisi-culpa-sit-optio-omnis-ipsum-pariatur", - "industry_id": "", - "size_id": "", - "vat_number": "157764830", - "id_number": "", - "number": "0001", - "shipping_address1": "5181", - "shipping_address2": "66797 Jedediah Isle Suite 479", - "shipping_city": "Lake Rosariomouth", - "shipping_state": "Nevada", - "shipping_postal_code": "31693", - "shipping_country_id": "4", - "settings": { - "currency_id": "1" - }, - "last_login": 0, - "custom_value1": "", - "custom_value2": "", - "custom_value3": "", - "custom_value4": "", - "contacts": [ - { - "first_name": "Rita", - "last_name": "Pouros", - "email": "user@example.com", - "password": "**********", - "phone": "+1-331-663-8498", - "contact_key": "hNQkBU6RM6tG2pwu4J7dCfuq2ZdH6Q8anEvKnyoL", - "is_primary": true, - "send_email": true, - "custom_value1": "", - "custom_value2": "", - "custom_value3": "", - "custom_value4": "", - "last_login": 0, - "link": "https://localhost:8080/client/key_login/hNQkBU6RM6tG2pwu4J7dCfuq2ZdH6Q8anEvKnyoL", - "created_at": 1612792539, - "updated_at": 1612792539, - "archived_at": 0, - "id": "VolejRejNm" - }, - { - "first_name": "Danika", - "last_name": "Hauck", - "email": "bbrakus@example.net", - "password": "**********", - "phone": "662-968-5275 x48146", - "contact_key": "4hWqvVUv2bwYIOb25rWmQhbhadnl5yneTzglGZ32", - "is_primary": false, - "send_email": true, - "custom_value1": "", - "custom_value2": "", - "custom_value3": "", - "custom_value4": "", - "last_login": 0, - "link": "https://localhost:8080/client/key_login/4hWqvVUv2bwYIOb25rWmQhbhadnl5yneTzglGZ32", - "created_at": 1612792539, - "updated_at": 1612792539, - "archived_at": 0, - "id": "Wpmbk5ezJn" - } - ], - "activities": [], - "ledger": [], - "gateway_tokens": [], - "documents": [], - "system_logs": [], - "created_at": 1612792539, - "updated_at": 1612792565, - "archived_at": 0, - "id": "VolejRejNm", - "isChanged": true, - "is_deleted": false, - "user_id": "VolejRejNm", - "assigned_user_id": "" - }; - - axios.put('https://localhost:8080/api/v1/company_gateways/VolejRejNm', gatewaysBody, {headers}) - axios.put('https://localhost:8080/api/v1/clients/VolejRejNm', clientBody, {headers}); // Set country to US. - - cy.viewport('macbook-13'); - cy.clientLogin(); - }); - - afterEach(() => { - cy.visit('/client/logout').visit('/client/login'); - }); - - it('should be able to pay using SOFORT', function () { - cy.visit('/client/invoices'); - - cy.get('[data-cy=pay-now]').first().click(); - cy.get('[data-cy=pay-now-dropdown]').click(); - cy.get('[data-cy=pay-with-2]').click(); - - cy.get('#pay-now').click(); - - cy.get('.common-ButtonGroup > .common-Button--default').click(); - - cy.url().should('contain', '/client/payments/'); - }); -}); diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js deleted file mode 100644 index dbf41bdde41f..000000000000 --- a/cypress/plugins/index.js +++ /dev/null @@ -1,24 +0,0 @@ -/// -// *********************************************************** -// 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 - - config.ignoreTestFiles = "**/examples/*.spec.js"; - return config; -} diff --git a/cypress/support/account.js b/cypress/support/account.js deleted file mode 100644 index 4be5996c72ff..000000000000 --- a/cypress/support/account.js +++ /dev/null @@ -1,35 +0,0 @@ -import axios from 'axios'; - -const baseUrl = Cypress.config().baseUrl.endsWith('/') - ? Cypress.config().baseUrl.slice(0, -1) - : Cypress.config().baseUrl; - -Cypress.Commands.add('createAdminAccount', () => { - let body = { - first_name: "Cypress", - last_name: "Testing", - email: "cypress_testing@example.com", - password: "password", - terms_of_service: true, - privacy_policy: true, - report_errors: true, - }; - - let headers = { - "Content-Type": "application/json", - "X-Requested-With": "XMLHttpRequest" - }; - - return axios.post(`${baseUrl}/api/v1/signup?first_load=true`, body, headers) - .then(response => { - console.log('Data from the request', response.data.data[0]); - return response.data.data[0]; - }) - .catch(e => { - throw "Unable to create an account for admin."; - }); -}); - -Cypress.Commands.add('createClientAccount', () => { - // .. -}); diff --git a/cypress/support/artisan.js b/cypress/support/artisan.js deleted file mode 100644 index d183e20ddce0..000000000000 --- a/cypress/support/artisan.js +++ /dev/null @@ -1,6 +0,0 @@ -Cypress.Commands.add('artisan', (cmd) => { - let environment = Cypress.env('runningEnvironment'); - let prefix = environment === 'docker' ? 'docker-compose run --rm artisan' : 'php artisan'; - - return cy.exec(`${prefix} ${cmd}`); -}); diff --git a/cypress/support/commands.js b/cypress/support/commands.js deleted file mode 100644 index 909d7beb0984..000000000000 --- a/cypress/support/commands.js +++ /dev/null @@ -1,108 +0,0 @@ -// *********************************************** -// This example commands.js shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** -// -// -// -- This is a parent command -- -// Cypress.Commands.add("login", (email, password) => { ... }) -// -// -// -- This is a child command -- -// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This will overwrite an existing command -- -// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) -const axios = require('axios'); -const fixture = require('../fixtures/example.json'); - -Cypress.Commands.add('clientLogin', () => { - cy.visit('/client/login'); - cy.get('#test_email') - .invoke('val') - .then((emailValue) => { - cy.get('#test_password') - .invoke('val') - .then((passwordValue) => { - cy.get('#email') - .type(emailValue) - .should('have.value', emailValue); - cy.get('#password') - .type(passwordValue) - .should('have.value', passwordValue); - cy.get('#loginBtn') - .contains('Login') - .click(); - }); - }); -}); - -Cypress.Commands.add('iframeLoaded', {prevSubject: 'element'}, ($iframe) => { - const contentWindow = $iframe.prop('contentWindow'); - return new Promise((resolve) => { - if (contentWindow) { - resolve(contentWindow); - } else { - $iframe.on('load', () => { - resolve(contentWindow); - }); - } - }); -}); - -Cypress.Commands.add( - 'getInDocument', - { - prevSubject: - 'Permission denied to access property "document" on cross-origin object', - }, - (document, selector) => Cypress.$(selector, document) -); - -Cypress.Commands.add('getWithinIframe', (targetElement) => - cy - .get('iframe') - .iframeLoaded() - .its('document') - .getInDocument(targetElement) -); - -Cypress.Commands.add('useGateway', (gateway) => { - let body = { - settings: { - entity: 'App\\Models\\Client', - industry_id: '', - size_id: '', - currency_id: '1', - company_gateway_ids: gateway, - }, - }; - - let options = { - headers: { - 'X-Api-Secret': 'superdoopersecrethere', - 'X-Api-Token': - 'S0x8behDk8HG8PI0i8RXdpf2AVud5b993pE8vata7xmm4RgW6u3NeGC8ibWIUjZv', - 'X-Requested-With': 'XMLHttpRequest', - }, - }; - - axios - .put( - `http://localhost:8000/api/v1/clients/${fixture.first}`, - body, - options - ) - .then((response) => console.log(response)) - .catch((error) => console.log(error.message)); -}); diff --git a/cypress/support/index.js b/cypress/support/index.js deleted file mode 100644 index 7874a4e0db1c..000000000000 --- a/cypress/support/index.js +++ /dev/null @@ -1,22 +0,0 @@ -// *********************************************************** -// 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 -// *********************************************************** - -// Import commands.js using ES2015 syntax: -import './commands'; -import './artisan'; -import './account'; - -// Alternatively you can use CommonJS syntax: -// require('./commands') diff --git a/dusk.sh b/dusk.sh new file mode 100644 index 000000000000..8e3cde4b6aa7 --- /dev/null +++ b/dusk.sh @@ -0,0 +1,64 @@ +#!/bin/bash +n=1 +TYPE=${!n} + +if [ -z "$TYPE" ]; then + TYPE="all" +fi + +echo "$ RUNNING: '$TYPE'" + +echo "$ php artisan optimize" +php artisan optimize + +echo "==========================================" + +GENERIC_TESTS=`find tests/Browser/ClientPortal/ -maxdepth 1 -type f -name '*.php'` + +if [ $TYPE == 'gateways' ]; then + GENERIC_TESTS="" + + echo "$ Skippping generic tests." + echo "==========================================" +fi + +for TEST_CLASS in $GENERIC_TESTS; do + echo "Test class: $TEST_CLASS" + + echo "$ php artisan migrate:fresh --seed" + php artisan migrate:fresh --seed &> /dev/null + + echo "$ php artisan ninja:create-single-account" + php artisan ninja:create-single-account &> /dev/null + + echo "$ php artisan dusk $TEST_CLASS" + php -d memory_limit=1G artisan dusk ${@:2} --stop-on-error --stop-on-failure $TEST_CLASS || exit 1 + + echo "==========================================" +done || exit 1 + +GATEWAY_TESTS=`find tests/Browser/ClientPortal/Gateways/ -type f -name '*.php'` + +if [ $TYPE == 'generic' ]; then + GATEWAY_TESTS="" + + echo "$ Skippping gateway tests." + echo "==========================================" +fi + +for TEST_CLASS in $GATEWAY_TESTS; do + echo "Test class: $TEST_CLASS" + + echo "$ php artisan migrate:fresh --seed" + php artisan migrate:fresh --seed &> /dev/null + + echo "$ php artisan ninja:create-single-account" + php artisan ninja:create-single-account &> /dev/null + + echo "$ php artisan dusk $TEST_CLASS" + php -d memory_limit=1G artisan dusk ${@:2} --stop-on-error --stop-on-failure $TEST_CLASS || exit 1 + + echo "==========================================" +done || exit 1 + +echo 'All tests completed successfully.' diff --git a/package-lock.json b/package-lock.json index 66f2e8126722..cfc4b05bdd1a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14461,83 +14461,6 @@ "to-fast-properties": "^2.0.0" } }, - "@cypress/listr-verbose-renderer": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz", - "integrity": "sha1-p3SS9LEdzHxEajSz4ochr9M8ZCo=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "cli-cursor": "^1.0.2", - "date-fns": "^1.27.2", - "figures": "^1.7.0" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "@cypress/request": { - "version": "2.88.5", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.5.tgz", - "integrity": "sha512-TzEC1XMi1hJkywWpRfD2clreTa/Z+lOrXDCxxBTBPEcY5azdPi56A6Xw+O4tWJnaJH3iIE7G5aDXZC6JgRZLcA==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "@cypress/xvfb": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", - "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", - "dev": true, - "requires": { - "debug": "^3.1.0", - "lodash.once": "^4.1.1" - } - }, "@fullhuman/postcss-purgecss": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-2.3.0.tgz", @@ -14561,15 +14484,6 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==" }, - "@samverschueren/stream-to-observable": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz", - "integrity": "sha512-c/qwwcHyafOQuVQJj0IlBjf5yYgBI7YPJ77k4fOJYesb41jio65eaJODRUmfYKhTOFBrIZ66kgvGPlNbjuoRdQ==", - "dev": true, - "requires": { - "any-observable": "^0.3.0" - } - }, "@tailwindcss/custom-forms": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/@tailwindcss/custom-forms/-/custom-forms-0.2.1.tgz", @@ -14626,18 +14540,6 @@ "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==" }, - "@types/sinonjs__fake-timers": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.1.tgz", - "integrity": "sha512-yYezQwGWty8ziyYLdZjwxyMb0CZR49h8JALHGrxjQHWlqGgc8kLdHEgWrgL0uZ29DMvEVBDnHU2Wg36zKSIUtA==", - "dev": true - }, - "@types/sizzle": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz", - "integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==", - "dev": true - }, "@vue/component-compiler-utils": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.2.0.tgz", @@ -14948,12 +14850,6 @@ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==" }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, "ansi-html": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", @@ -14972,12 +14868,6 @@ "color-convert": "^1.9.0" } }, - "any-observable": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz", - "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==", - "dev": true - }, "anymatch": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", @@ -15002,12 +14892,6 @@ "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" }, - "arch": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.1.2.tgz", - "integrity": "sha1-DFK75zRLtPomDEQ9LLrZwA/y8L8=", - "dev": true - }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -15064,15 +14948,6 @@ "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, "asn1.js": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", @@ -15115,12 +14990,6 @@ } } }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", @@ -15131,12 +15000,6 @@ "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.6.tgz", "integrity": "sha1-ECyenpAF0+fjgpvwxPok7oYu6bk=" }, - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha1-s6JoXF67ZB094C0WEALGD8n4VyA=", - "dev": true - }, "async-each": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", @@ -15147,12 +15010,6 @@ "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -15200,18 +15057,6 @@ } } }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz", - "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==", - "dev": true - }, "axios": { "version": "0.21.1", "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", @@ -15360,15 +15205,6 @@ "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=" }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -15603,12 +15439,6 @@ "isarray": "^1.0.0" } }, - "buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true - }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", @@ -15685,12 +15515,6 @@ "unset-value": "^1.0.0" } }, - "cachedir": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", - "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", - "dev": true - }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -15774,12 +15598,6 @@ "credit-card-type": "^8.0.0" } }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -15795,12 +15613,6 @@ "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" }, - "check-more-types": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", - "integrity": "sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA=", - "dev": true - }, "chokidar": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", @@ -15833,12 +15645,6 @@ "tslib": "^1.9.0" } }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, "cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", @@ -15889,58 +15695,6 @@ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" }, - "cli-cursor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", - "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", - "dev": true, - "requires": { - "restore-cursor": "^1.0.1" - } - }, - "cli-table3": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz", - "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==", - "dev": true, - "requires": { - "colors": "^1.1.2", - "object-assign": "^4.1.0", - "string-width": "^2.1.1" - } - }, - "cli-truncate": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", - "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=", - "dev": true, - "requires": { - "slice-ansi": "0.0.4", - "string-width": "^1.0.1" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, "cliui": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", @@ -16006,12 +15760,6 @@ "q": "^1.1.2" } }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, "collect.js": { "version": "4.28.6", "resolved": "https://registry.npmjs.org/collect.js/-/collect.js-4.28.6.tgz", @@ -16062,34 +15810,6 @@ "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==" }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true, - "optional": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha1-n9YCvZNilOnp70aj9NaWQESxgGg=", - "dev": true - }, - "common-tags": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz", - "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==", - "dev": true - }, "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -16641,83 +16361,6 @@ "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=" }, - "cypress": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-4.12.1.tgz", - "integrity": "sha512-9SGIPEmqU8vuRA6xst2CMTYd9sCFCxKSzrHt0wr+w2iAQMCIIsXsQ5Gplns1sT6LDbZcmLv6uehabAOl3fhc9Q==", - "dev": true, - "requires": { - "@cypress/listr-verbose-renderer": "^0.4.1", - "@cypress/request": "^2.88.5", - "@cypress/xvfb": "^1.2.4", - "@types/sinonjs__fake-timers": "^6.0.1", - "@types/sizzle": "^2.3.2", - "arch": "^2.1.2", - "bluebird": "^3.7.2", - "cachedir": "^2.3.0", - "chalk": "^2.4.2", - "check-more-types": "^2.24.0", - "cli-table3": "~0.5.1", - "commander": "^4.1.1", - "common-tags": "^1.8.0", - "debug": "^4.1.1", - "eventemitter2": "^6.4.2", - "execa": "^1.0.0", - "executable": "^4.1.1", - "extract-zip": "^1.7.0", - "fs-extra": "^8.1.0", - "getos": "^3.2.1", - "is-ci": "^2.0.0", - "is-installed-globally": "^0.3.2", - "lazy-ass": "^1.6.0", - "listr": "^0.14.3", - "lodash": "^4.17.19", - "log-symbols": "^3.0.0", - "minimist": "^1.2.5", - "moment": "^2.27.0", - "ospath": "^1.2.2", - "pretty-bytes": "^5.3.0", - "ramda": "~0.26.1", - "request-progress": "^3.0.0", - "supports-color": "^7.1.0", - "tmp": "~0.1.0", - "untildify": "^4.0.0", - "url": "^0.11.0", - "yauzl": "^2.10.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "d": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", @@ -16727,21 +16370,6 @@ "type": "^1.0.1" } }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "date-fns": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", - "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", - "dev": true - }, "de-indent": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", @@ -16883,12 +16511,6 @@ } } }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -17040,16 +16662,6 @@ "stream-shift": "^1.0.0" } }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -17060,12 +16672,6 @@ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.376.tgz", "integrity": "sha512-cv/PYVz5szeMz192ngilmezyPNFkUjuynuL2vNdiqIrio440nfTDdc0JJU0TS2KHLSVCs9gBbt4CFqM+HcBnjw==" }, - "elegant-spinner": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", - "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=", - "dev": true - }, "elliptic": { "version": "6.5.4", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", @@ -17293,12 +16899,6 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, - "eventemitter2": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.3.tgz", - "integrity": "sha512-t0A2msp6BzOf+QAcI6z9XMktLj52OjGQg+8SJH6v5+3uxNpWYRR3wQmfA+6xtMU9kOC59qk9licus5dYcrYkMQ==", - "dev": true - }, "eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -17380,21 +16980,6 @@ } } }, - "executable": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", - "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", - "dev": true, - "requires": { - "pify": "^2.2.0" - } - }, - "exit-hook": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", - "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", - "dev": true - }, "expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", @@ -17520,12 +17105,6 @@ } } }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, "extend-shallow": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", @@ -17629,35 +17208,6 @@ } } }, - "extract-zip": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", - "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", - "dev": true, - "requires": { - "concat-stream": "^1.6.2", - "debug": "^2.6.9", - "mkdirp": "^0.5.4", - "yauzl": "^2.10.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, "fast-deep-equal": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", @@ -17694,30 +17244,11 @@ "websocket-driver": ">=0.5.1" } }, - "fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", - "dev": true, - "requires": { - "pend": "~1.2.0" - } - }, "figgy-pudding": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==" }, - "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" - } - }, "file-loader": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-2.0.0.tgz", @@ -17852,23 +17383,6 @@ "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", @@ -18012,24 +17526,6 @@ "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" }, - "getos": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", - "integrity": "sha1-ATTR9OAOtGFExanArE3Ah8uyfcU=", - "dev": true, - "requires": { - "async": "^3.2.0" - } - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -18067,15 +17563,6 @@ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=" }, - "global-dirs": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz", - "integrity": "sha1-rN87tmhbzVXLNeigUiZlaelGkgE=", - "dev": true, - "requires": { - "ini": "^1.3.5" - } - }, "global-modules": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", @@ -18167,36 +17654,6 @@ "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "dev": true, - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "dependencies": { - "ajv": { - "version": "6.12.4", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", - "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - } - } - }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -18445,17 +17902,6 @@ "micromatch": "^3.1.10" } }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, "https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", @@ -18636,12 +18082,6 @@ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" }, - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", - "dev": true - }, "indexes-of": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", @@ -18763,15 +18203,6 @@ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==" }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, "is-color-stop": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", @@ -18861,16 +18292,6 @@ "is-extglob": "^2.1.1" } }, - "is-installed-globally": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", - "integrity": "sha1-/T76ee5nDRGHIzGC1bCh3QAxMUE=", - "dev": true, - "requires": { - "global-dirs": "^2.0.1", - "is-path-inside": "^3.0.1" - } - }, "is-negative-zero": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", @@ -18899,15 +18320,6 @@ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" }, - "is-observable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-1.1.0.tgz", - "integrity": "sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==", - "dev": true, - "requires": { - "symbol-observable": "^1.1.0" - } - }, "is-path-cwd": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", @@ -18931,12 +18343,6 @@ } } }, - "is-path-inside": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", - "integrity": "sha1-9SIPyCo+IzdXKR3dycWHfyofMBc=", - "dev": true - }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -18945,12 +18351,6 @@ "isobject": "^3.0.1" } }, - "is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", - "dev": true - }, "is-regex": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz", @@ -18986,12 +18386,6 @@ "has-symbols": "^1.0.1" } }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -19017,12 +18411,6 @@ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, "jest-worker": { "version": "25.5.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.5.0.tgz", @@ -19068,12 +18456,6 @@ } } }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -19089,23 +18471,11 @@ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, "json3": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", @@ -19127,18 +18497,6 @@ "graceful-fs": "^4.1.6" } }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, "killable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", @@ -19224,12 +18582,6 @@ "webpack-sources": "^1.1.0" } }, - "lazy-ass": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", - "integrity": "sha1-eZllXoZGwX8In90YfRUNMyTVRRM=", - "dev": true - }, "linkify-urls": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/linkify-urls/-/linkify-urls-3.1.1.tgz", @@ -19238,132 +18590,6 @@ "create-html-element": "^2.1.0" } }, - "listr": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz", - "integrity": "sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==", - "dev": true, - "requires": { - "@samverschueren/stream-to-observable": "^0.3.0", - "is-observable": "^1.1.0", - "is-promise": "^2.1.0", - "is-stream": "^1.1.0", - "listr-silent-renderer": "^1.1.1", - "listr-update-renderer": "^0.5.0", - "listr-verbose-renderer": "^0.5.0", - "p-map": "^2.0.0", - "rxjs": "^6.3.3" - } - }, - "listr-silent-renderer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz", - "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=", - "dev": true - }, - "listr-update-renderer": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz", - "integrity": "sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "cli-truncate": "^0.2.1", - "elegant-spinner": "^1.0.1", - "figures": "^1.7.0", - "indent-string": "^3.0.0", - "log-symbols": "^1.0.2", - "log-update": "^2.3.0", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "log-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", - "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", - "dev": true, - "requires": { - "chalk": "^1.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "listr-verbose-renderer": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz", - "integrity": "sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "cli-cursor": "^2.1.0", - "date-fns": "^1.27.2", - "figures": "^2.0.0" - }, - "dependencies": { - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true, - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - } - } - }, "loader-runner": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", @@ -19407,12 +18633,6 @@ "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" }, - "lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=", - "dev": true - }, "lodash.toarray": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", @@ -19423,56 +18643,6 @@ "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" }, - "log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2" - } - }, - "log-update": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz", - "integrity": "sha1-iDKP19HOeTiykoN0bwsbwSayRwg=", - "dev": true, - "requires": { - "ansi-escapes": "^3.0.0", - "cli-cursor": "^2.0.0", - "wrap-ansi": "^3.0.1" - }, - "dependencies": { - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true, - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - } - } - }, "loglevel": { "version": "1.7.1", "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.7.1.tgz", @@ -19663,12 +18833,6 @@ "mime-db": "1.44.0" } }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha1-ggyGo5M0ZA6ZUWkovQP8qIBX0CI=", - "dev": true - }, "mini-svg-data-uri": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.2.3.tgz", @@ -19763,12 +18927,6 @@ "minimist": "^1.2.5" } }, - "moment": { - "version": "2.27.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz", - "integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==", - "dev": true - }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -20010,18 +19168,6 @@ "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=" }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -20161,12 +19307,6 @@ "wrappy": "1" } }, - "onetime": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", - "dev": true - }, "opn": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", @@ -20197,12 +19337,6 @@ "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" }, - "ospath": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", - "integrity": "sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs=", - "dev": true - }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -20373,29 +19507,11 @@ "sha.js": "^2.4.8" } }, - "pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "dev": true - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, "picomatch": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==" }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, "pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", @@ -21113,12 +20229,6 @@ "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", "optional": true }, - "pretty-bytes": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.3.0.tgz", - "integrity": "sha512-hjGrh+P926p4R4WbaB6OckyRtO0F0/lQBiT+0gnxjV+5kjPBrfVBFCsCLbMqVQeydvIoouYTCmmEURiH3R1Bdg==", - "dev": true - }, "pretty-hrtime": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", @@ -21163,12 +20273,6 @@ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true - }, "public-encrypt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", @@ -21247,12 +20351,6 @@ "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha1-yzroBuh0BERYTvFUzo7pjUA/PjY=", - "dev": true - }, "querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", @@ -21268,12 +20366,6 @@ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" }, - "ramda": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz", - "integrity": "sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==", - "dev": true - }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -21472,15 +20564,6 @@ "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==" }, - "request-progress": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", - "integrity": "sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4=", - "dev": true, - "requires": { - "throttleit": "^1.0.0" - } - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -21608,16 +20691,6 @@ } } }, - "restore-cursor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", - "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", - "dev": true, - "requires": { - "exit-hook": "^1.0.0", - "onetime": "^1.0.0" - } - }, "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", @@ -21684,15 +20757,6 @@ "aproba": "^1.1.1" } }, - "rxjs": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz", - "integrity": "sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -22068,12 +21132,6 @@ "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=" }, - "slice-ansi": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", - "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", - "dev": true - }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -22343,23 +21401,6 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, "ssri": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/ssri/-/ssri-7.1.0.tgz", @@ -22580,12 +21621,6 @@ "util.promisify": "~1.0.0" } }, - "symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", - "dev": true - }, "tailwindcss": { "version": "1.9.6", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-1.9.6.tgz", @@ -22752,12 +21787,6 @@ } } }, - "throttleit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", - "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", - "dev": true - }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -22790,15 +21819,6 @@ "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" }, - "tmp": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", - "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", - "dev": true, - "requires": { - "rimraf": "^2.6.3" - } - }, "to-arraybuffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", @@ -22852,16 +21872,6 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, "traverse": { "version": "0.6.6", "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", @@ -22877,21 +21887,6 @@ "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true - }, "type": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", @@ -23056,12 +22051,6 @@ } } }, - "untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "dev": true - }, "upath": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", @@ -23191,17 +22180,6 @@ "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==" }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, "vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", @@ -23949,33 +22927,6 @@ "errno": "~0.1.7" } }, - "wrap-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz", - "integrity": "sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=", - "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -24115,16 +23066,6 @@ "camelcase": "^5.0.0", "decamelize": "^1.2.0" } - }, - "yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", - "dev": true, - "requires": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } } } } diff --git a/package.json b/package.json index 6127b133ac46..cca4b91a0b94 100644 --- a/package.json +++ b/package.json @@ -7,14 +7,11 @@ "watch-poll": "npm run watch -- --watch-poll", "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", "prod": "npm run production", - "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", - "test": "cypress run", - "test:gui": "cypress open" + "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" }, "devDependencies": { "@babel/compat-data": "7.9.0", "@babel/plugin-proposal-class-properties": "^7.13.0", - "cypress": "^4.12.1", "laravel-mix-purgecss": "^5.0.0", "vue-template-compiler": "^2.6.12" }, diff --git a/resources/views/portal/ninja2020/components/general/sidebar/header.blade.php b/resources/views/portal/ninja2020/components/general/sidebar/header.blade.php index ee232d1952c8..e829b120f9f3 100644 --- a/resources/views/portal/ninja2020/components/general/sidebar/header.blade.php +++ b/resources/views/portal/ninja2020/components/general/sidebar/header.blade.php @@ -23,7 +23,10 @@ @foreach($multiple_contacts as $contact) - {{ $contact->company->present()->name() }} - {{ $contact->client->present()->name()}} + {{ $contact->company->present()->name() }} + - {{ $contact->client->present()->name()}} @endforeach @@ -32,18 +35,28 @@ @endif - - + + {{ auth()->user()->present()->name() }} - + - + {{ ctrans('texts.profile') }} - + {{ ctrans('texts.logout') }} diff --git a/resources/views/portal/ninja2020/components/livewire/invoices-table.blade.php b/resources/views/portal/ninja2020/components/livewire/invoices-table.blade.php index 617a72fd442c..f4500f3fe41d 100644 --- a/resources/views/portal/ninja2020/components/livewire/invoices-table.blade.php +++ b/resources/views/portal/ninja2020/components/livewire/invoices-table.blade.php @@ -99,7 +99,7 @@ @csrf - + {{ ctrans('texts.pay_now') }} diff --git a/resources/views/portal/ninja2020/components/livewire/pay-now-dropdown.blade.php b/resources/views/portal/ninja2020/components/livewire/pay-now-dropdown.blade.php index d0c53470b6fb..259f7f11e75e 100644 --- a/resources/views/portal/ninja2020/components/livewire/pay-now-dropdown.blade.php +++ b/resources/views/portal/ninja2020/components/livewire/pay-now-dropdown.blade.php @@ -1,10 +1,10 @@ @unless(count($methods) == 0) + class="relative inline-block text-left" dusk="payment-methods-dropdown"> - {{ ctrans('texts.pay_now') }} @@ -20,19 +20,19 @@ @foreach($methods as $index => $method) @if($method['label'] == 'Custom') - + dusk="payment-method"> {{ \App\Models\CompanyGateway::find($method['company_gateway_id'])->firstOrFail()->getConfigField('name') }} @elseif($total > 0) - + dusk="payment-method"> {{ $method['label'] }} @endif diff --git a/resources/views/portal/ninja2020/gateways/stripe/ach/verify.blade.php b/resources/views/portal/ninja2020/gateways/stripe/ach/verify.blade.php index e173a2dd84a9..46afb0bd3d9a 100644 --- a/resources/views/portal/ninja2020/gateways/stripe/ach/verify.blade.php +++ b/resources/views/portal/ninja2020/gateways/stripe/ach/verify.blade.php @@ -11,11 +11,11 @@ @component('portal.ninja2020.components.general.card-element', ['title' => '#1 ' . ctrans('texts.amount_cents')]) - + @endcomponent @component('portal.ninja2020.components.general.card-element', ['title' => '#2 ' . ctrans('texts.amount_cents')]) - + @endcomponent @component('portal.ninja2020.gateways.includes.pay_now', ['type' => 'submit']) diff --git a/resources/views/portal/ninja2020/invoices/payment.blade.php b/resources/views/portal/ninja2020/invoices/payment.blade.php index 2673b298d1c8..4f2b8451855f 100644 --- a/resources/views/portal/ninja2020/invoices/payment.blade.php +++ b/resources/views/portal/ninja2020/invoices/payment.blade.php @@ -100,6 +100,7 @@ type="text" class="input mt-0 mr-4 relative" name="payable_invoices[{{$key}}][amount]" + dusk="underpayment-input" value="{{ $invoice->partial > 0 ? $invoice->partial : $invoice->balance }}"/> diff --git a/resources/views/portal/ninja2020/payment_methods/includes/modals/removal.blade.php b/resources/views/portal/ninja2020/payment_methods/includes/modals/removal.blade.php index 3c6324dd804d..d80ed480ca62 100644 --- a/resources/views/portal/ninja2020/payment_methods/includes/modals/removal.blade.php +++ b/resources/views/portal/ninja2020/payment_methods/includes/modals/removal.blade.php @@ -36,7 +36,7 @@ @csrf @method('DELETE') - + {{ ctrans('texts.remove') }} diff --git a/resources/views/portal/ninja2020/profile/settings/general.blade.php b/resources/views/portal/ninja2020/profile/settings/general.blade.php index c891f046160e..259f0c934ce4 100644 --- a/resources/views/portal/ninja2020/profile/settings/general.blade.php +++ b/resources/views/portal/ninja2020/profile/settings/general.blade.php @@ -7,7 +7,7 @@ - + @csrf @method('PUT') @@ -15,7 +15,9 @@ @lang('texts.first_name') - + @error('first_name') {{ $message }} @@ -25,7 +27,9 @@ @lang('texts.last_name') - + @error('last_name') {{ $message }} @@ -35,7 +39,9 @@ @lang('texts.email_address') - + @error('email') {{ $message }} @@ -44,8 +50,9 @@ - @lang('texts.phone') - + @lang('texts.phone') + @error('phone') {{ $message }} @@ -54,8 +61,9 @@ - @lang('texts.password') - + @lang('texts.password') + @error('password') {{ $message }} @@ -64,8 +72,11 @@ - @lang('texts.confirm_password') - + @lang('texts.confirm_password') + @error('password_confirmation') {{ $message }} @@ -75,7 +86,8 @@ - {{ $saved }} + {{ $saved }} diff --git a/resources/views/portal/ninja2020/profile/settings/name-website-logo.blade.php b/resources/views/portal/ninja2020/profile/settings/name-website-logo.blade.php index cd1f88afcb9d..a99547730af9 100644 --- a/resources/views/portal/ninja2020/profile/settings/name-website-logo.blade.php +++ b/resources/views/portal/ninja2020/profile/settings/name-website-logo.blade.php @@ -13,45 +13,49 @@ - {{ ctrans('texts.name') }} - + {{ ctrans('texts.name') }} + @error('name') - - {{ $message }} - + + {{ $message }} + @enderror - {{ ctrans('texts.vat_number') }} - + {{ ctrans('texts.vat_number') }} + @error('vat_number') - - {{ $message }} - + + {{ $message }} + @enderror - {{ ctrans('texts.phone') }} - + {{ ctrans('texts.phone') }} + @error('phone') - - {{ $message }} - + + {{ $message }} + @enderror - {{ ctrans('texts.website') }} + {{ ctrans('texts.website') }} E.g. https://example.com - + @error('website') - - {{ $message }} - + + {{ $message }} + @enderror diff --git a/resources/views/portal/ninja2020/profile/settings/personal-address.blade.php b/resources/views/portal/ninja2020/profile/settings/personal-address.blade.php index 53c51709a6d3..e4b9ffc6e4fd 100644 --- a/resources/views/portal/ninja2020/profile/settings/personal-address.blade.php +++ b/resources/views/portal/ninja2020/profile/settings/personal-address.blade.php @@ -6,7 +6,7 @@ - + @csrf diff --git a/resources/views/portal/ninja2020/profile/settings/shipping-address.blade.php b/resources/views/portal/ninja2020/profile/settings/shipping-address.blade.php index 9129fabddef6..82d15ebfde69 100644 --- a/resources/views/portal/ninja2020/profile/settings/shipping-address.blade.php +++ b/resources/views/portal/ninja2020/profile/settings/shipping-address.blade.php @@ -6,7 +6,7 @@ - + @csrf diff --git a/tests/Browser/ClientPortal/CreditsTest.php b/tests/Browser/ClientPortal/CreditsTest.php new file mode 100644 index 000000000000..c5c274b44461 --- /dev/null +++ b/tests/Browser/ClientPortal/CreditsTest.php @@ -0,0 +1,45 @@ +driver->manage()->deleteAllCookies(); + } + + $this->browse(function (Browser $browser) { + $browser + ->visit(new Login()) + ->auth(); + }); + } + + public function testPageLoads() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.credits.index') + ->assertSeeIn('span[data-ref="meta-title"]', 'Credits') + ->visitRoute('client.logout'); + }); + } +} diff --git a/tests/Browser/ClientPortal/DocumentsTest.php b/tests/Browser/ClientPortal/DocumentsTest.php new file mode 100644 index 000000000000..454acd856b59 --- /dev/null +++ b/tests/Browser/ClientPortal/DocumentsTest.php @@ -0,0 +1,45 @@ +driver->manage()->deleteAllCookies(); + } + + $this->browse(function (Browser $browser) { + $browser + ->visit(new Login()) + ->auth(); + }); + } + + public function testPageLoads() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.documents.index') + ->assertSee('Documents') + ->visitRoute('client.logout'); + }); + } +} diff --git a/tests/Browser/ClientPortal/Gateways/AuthorizeNet/CreditCardTest.php b/tests/Browser/ClientPortal/Gateways/AuthorizeNet/CreditCardTest.php new file mode 100644 index 000000000000..d8030f5701ea --- /dev/null +++ b/tests/Browser/ClientPortal/Gateways/AuthorizeNet/CreditCardTest.php @@ -0,0 +1,127 @@ +markTestSkipped('Skipping Authorize.net (GitHub Actions)'); + } + + foreach (static::$browsers as $browser) { + $browser->driver->manage()->deleteAllCookies(); + } + + $this->browse(function (Browser $browser) { + $browser + ->visit(new Login()) + ->auth(); + }); + + $this->disableCompanyGateways(); + + CompanyGateway::where('gateway_key', '3b6621f970ab18887c4f6dca78d3f8bb')->restore(); + } + + public function testPayWithNewCard() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.invoices.index') + ->click('@pay-now') + ->press('Pay Now') + ->clickLink('Credit Card') + ->type('card-number', '4007000000027') + ->type('card-holders-name', 'John Doe') + ->type('.expiry', '12/28') + ->type('cvc', '100') + ->press('Pay Now') + ->waitForText('Details of the payment', 60); + }); + } + + public function testPayWithNewCardAndSaveForFutureUse() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.invoices.index') + ->click('@pay-now') + ->press('Pay Now') + ->clickLink('Credit Card') + ->radio('#proxy_is_default', true) + ->type('card-number', '4007000000027') + ->type('card-holders-name', 'John Doe') + ->type('.expiry', '12/28') + ->type('cvc', '100') + ->press('Pay Now') + ->waitForText('Details of the payment', 60) + ->visitRoute('client.payment_methods.index') + ->clickLink('View') + ->assertSee('0027'); + }); + } + + public function testPayWithSavedCard() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.invoices.index') + ->click('@pay-now') + ->press('Pay Now') + ->clickLink('Credit Card') + ->click('.toggle-payment-with-token') + ->press('Pay Now') + ->waitForText('Details of the payment', 60); + }); + } + + public function testRemoveCard() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.payment_methods.index') + ->clickLink('View') + ->press('Remove Payment Method') + ->waitForText('Confirmation') + ->click('@confirm-payment-removal') + ->assertSee('Payment method has been successfully removed.'); + }); + } + + public function testAddingCreditCardStandalone() + { + $this->markTestIncomplete("E00117 OTS Service Error 'Field validation error.'"); + + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.payment_methods.index') + ->press('Add Payment Method') + ->clickLink('Credit Card') + ->type('card-number', '4012888818888') + ->type('card-holders-name', 'John Doe') + ->type('.expiry', '12/28') + ->type('cvc', '900') + ->press('Add Payment Method') + ->waitForText('0027', 60); + }); + } +} diff --git a/tests/Browser/ClientPortal/Gateways/Braintree/CreditCardTest.php b/tests/Browser/ClientPortal/Gateways/Braintree/CreditCardTest.php new file mode 100644 index 000000000000..ef125ab77981 --- /dev/null +++ b/tests/Browser/ClientPortal/Gateways/Braintree/CreditCardTest.php @@ -0,0 +1,146 @@ +driver->manage()->deleteAllCookies(); + } + + $this->browse(function (Browser $browser) { + $browser + ->visit(new Login()) + ->auth(); + }); + + $this->disableCompanyGateways(); + + CompanyGateway::where('gateway_key', 'f7ec488676d310683fb51802d076d713')->restore(); + + $cg = CompanyGateway::where('gateway_key', 'f7ec488676d310683fb51802d076d713')->firstOrFail(); + $fees_and_limits = $cg->fees_and_limits; + $fees_and_limits->{GatewayType::CREDIT_CARD} = new FeesAndLimits(); + $cg->fees_and_limits = $fees_and_limits; + $cg->save(); + + $company = Company::first(); + $settings = $company->settings; + + $settings->client_portal_allow_under_payment = true; + $settings->client_portal_allow_over_payment = true; + + $company->settings = $settings; + $company->save(); + } + + public function testPayWithNewCard() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.invoices.index') + ->click('@pay-now') + ->type('@underpayment-input', '100') + ->press('Pay Now') + ->clickLink('Credit Card') + ->waitFor('#braintree-hosted-field-number', 60) + ->withinFrame('#braintree-hosted-field-number', function (Browser $browser) { + $browser->type('credit-card-number', '4111111111111111'); + }) + ->withinFrame('#braintree-hosted-field-expirationDate', function (Browser $browser) { + $browser->type('expiration', '04/25'); + }) + ->press('Pay Now') + ->waitForText('Details of the payment', 60); + }); + } + + public function testPayWithNewCardAndSaveForFuture() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.invoices.index') + ->click('@pay-now') + ->type('@underpayment-input', '100') + ->press('Pay Now') + ->clickLink('Credit Card') + ->waitFor('#braintree-hosted-field-number', 60) + ->withinFrame('#braintree-hosted-field-number', function (Browser $browser) { + $browser->typeSlowly('credit-card-number', '4005519200000004'); + }) + ->withinFrame('#braintree-hosted-field-expirationDate', function (Browser $browser) { + $browser->typeSlowly('expiration', '04/25'); + }) + ->radio('#proxy_is_default', true) + ->press('Pay Now') + ->waitForText('Details of the payment', 60) + ->visitRoute('client.payment_methods.index') + ->clickLink('View') + ->assertSee('0004'); + }); + } + + public function testPayWithSavedCard() + { + $this->markTestSkipped('Works in "real" browser, otherwise giving error code 0.'); + + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.invoices.index') + ->click('@pay-now') + ->type('@underpayment-input', '100') + ->press('Pay Now') + ->clickLink('Credit Card') + ->click('.toggle-payment-with-token') + ->click('#pay-now-with-token') + ->waitForText('Details of the payment', 60); + }); + } + + public function testRemoveCreditCard() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.payment_methods.index') + ->clickLink('View') + ->press('Remove Payment Method') + ->waitForText('Confirmation') + ->click('@confirm-payment-removal') + ->assertSee('Payment method has been successfully removed.'); + }); + } + + public function testAddingPaymentMethodShouldntBePossible() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.payment_methods.index') + ->press('Add Payment Method') + ->clickLink('Credit Card') + ->assertSee('This payment method can be can saved for future use, once you complete your first transaction. Don\'t forget to check "Store credit card details" during payment process.'); + }); + } +} diff --git a/tests/Browser/ClientPortal/Gateways/Braintree/PayPalTest.php b/tests/Browser/ClientPortal/Gateways/Braintree/PayPalTest.php new file mode 100644 index 000000000000..031d0c4a2e21 --- /dev/null +++ b/tests/Browser/ClientPortal/Gateways/Braintree/PayPalTest.php @@ -0,0 +1,45 @@ +driver->manage()->deleteAllCookies(); + } + + $this->browse(function (Browser $browser) { + $browser + ->visit(new Login()) + ->auth(); + }); + + $this->disableCompanyGateways(); + + CompanyGateway::where('gateway_key', 'f7ec488676d310683fb51802d076d713')->restore(); + } + + public function testOffsitePayment() + { + $this->markTestSkipped('Sometimes after redirect PayPal shows the register-like page with credit card, sometimes is login page.'); + } +} diff --git a/tests/Browser/ClientPortal/Gateways/CheckoutCom/CreditCardTest.php b/tests/Browser/ClientPortal/Gateways/CheckoutCom/CreditCardTest.php new file mode 100644 index 000000000000..691779f30241 --- /dev/null +++ b/tests/Browser/ClientPortal/Gateways/CheckoutCom/CreditCardTest.php @@ -0,0 +1,120 @@ +driver->manage()->deleteAllCookies(); + } + + $this->disableCompanyGateways(); + + CompanyGateway::where('gateway_key', '3758e7f7c6f4cecf0f4f348b9a00f456')->restore(); + + $this->browse(function (Browser $browser) { + $browser + ->visit(new Login()) + ->auth(); + }); + } + + public function testAddingPaymentMethodShouldntBePossible() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.payment_methods.index') + ->press('Add Payment Method') + ->clickLink('Credit Card') + ->assertSee('Checkout.com can be can saved as payment method for future use, once you complete your first transaction. Don\'t forget to check "Store credit card details" during payment process.'); + }); + } + + public function testPayWithNewCard() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.invoices.index') + ->click('@pay-now') + ->press('Pay Now') + ->clickLink('Credit Card') + ->withinFrame('iframe', function (Browser $browser) { + $browser + ->type('cardnumber', '4242424242424242') + ->type('exp-date', '04/22') + ->type('cvc', '100'); + }) + ->press('#pay-button') + ->waitForText('Details of the payment', 60); + }); + } + + public function testPayWithNewCardAndSaveForFutureUse() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.invoices.index') + ->click('@pay-now') + ->press('Pay Now') + ->clickLink('Credit Card') + ->withinFrame('iframe', function (Browser $browser) { + $browser + ->type('cardnumber', '4242424242424242') + ->type('exp-date', '04/22') + ->type('cvc', '100'); + }) + ->radio('#proxy_is_default', true) + ->press('#pay-button') + ->waitForText('Details of the payment', 60) + ->visitRoute('client.payment_methods.index') + ->clickLink('View') + ->assertSee('4242'); + }); + } + + public function testPayWithSavedCreditCard() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.invoices.index') + ->click('@pay-now') + ->press('Pay Now') + ->clickLink('Credit Card') + ->click('.toggle-payment-with-token') + ->click('#pay-now-with-token') + ->waitForText('Details of the payment', 60); + }); + } + + public function testRemoveCreditCard() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.payment_methods.index') + ->clickLink('View') + ->press('Remove Payment Method') + ->waitForText('Confirmation') + ->click('@confirm-payment-removal') + ->assertSee('Payment method has been successfully removed.'); + }); + } +} diff --git a/tests/Browser/ClientPortal/Gateways/PayPal/PayPalTest.php b/tests/Browser/ClientPortal/Gateways/PayPal/PayPalTest.php new file mode 100644 index 000000000000..0525406c80de --- /dev/null +++ b/tests/Browser/ClientPortal/Gateways/PayPal/PayPalTest.php @@ -0,0 +1,40 @@ +driver->manage()->deleteAllCookies(); + } + + $this->browse(function (Browser $browser) { + $browser + ->visit(new Login()) + ->auth(); + }); + } + + public function testOffsitePayment() + { + $this->markTestSkipped('Sometimes after redirect PayPal shows the register-like page with credit card, sometimes is login page.'); + } +} diff --git a/tests/Browser/ClientPortal/Gateways/Stripe/ACHTest.php b/tests/Browser/ClientPortal/Gateways/Stripe/ACHTest.php new file mode 100644 index 000000000000..53947540b35b --- /dev/null +++ b/tests/Browser/ClientPortal/Gateways/Stripe/ACHTest.php @@ -0,0 +1,106 @@ +driver->manage()->deleteAllCookies(); + } + + $this->browse(function (Browser $browser) { + $browser + ->visit(new Login()) + ->auth(); + }); + + $this->disableCompanyGateways(); + + // Enable Stripe. + CompanyGateway::where('gateway_key', 'd14dd26a37cecc30fdd65700bfb55b23')->restore(); + + // Enable ACH. + $cg = CompanyGateway::where('gateway_key', 'd14dd26a37cecc30fdd65700bfb55b23')->firstOrFail(); + $fees_and_limits = $cg->fees_and_limits; + $fees_and_limits->{GatewayType::BANK_TRANSFER} = new FeesAndLimits(); + $cg->fees_and_limits = $fees_and_limits; + $cg->save(); + + // ACH required US to be billing country. + $client = Client::first(); + $client->country_id = 840; + $client->save(); + } + + public function testAddingACHAccountAndVerifyingIt() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.payment_methods.index') + ->press('Add Payment Method') + ->clickLink('Bank Account') + ->type('#account-holder-name', 'John Doe') + ->select('#country', 'US') + ->select('#currency', 'USD') + ->type('#routing-number', '110000000') + ->type('#account-number', '000123456789') + ->check('#accept-terms') + ->press('Add Payment Method') + ->waitForText('ACH (Verification)', 60) + ->type('@verification-1st', '32') + ->type('@verification-2nd', '45') + ->press('Complete Verification') + ->assertSee('Verification completed successfully') + ->assertSee('Bank Transfer'); + }); + } + + public function testPayingWithExistingACH() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.invoices.index') + ->click('@pay-now') + ->press('Pay Now') + ->clickLink('Bank Transfer') + ->click('.toggle-payment-with-token') + ->press('Pay Now') + ->waitForText('Details of the payment', 60); + }); + } + + public function testRemoveACHAccount() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.payment_methods.index') + ->clickLink('View') + ->press('Remove Payment Method') + ->waitForText('Confirmation') + ->click('@confirm-payment-removal') + ->assertSee('Payment method has been successfully removed.'); + }); + } +} diff --git a/tests/Browser/ClientPortal/Gateways/Stripe/AlipayTest.php b/tests/Browser/ClientPortal/Gateways/Stripe/AlipayTest.php new file mode 100644 index 000000000000..7a6a4630b8e2 --- /dev/null +++ b/tests/Browser/ClientPortal/Gateways/Stripe/AlipayTest.php @@ -0,0 +1,72 @@ +driver->manage()->deleteAllCookies(); + } + + $this->browse(function (Browser $browser) { + $browser + ->visit(new Login()) + ->auth(); + }); + + $this->disableCompanyGateways(); + + // Enable Stripe. + CompanyGateway::where('gateway_key', 'd14dd26a37cecc30fdd65700bfb55b23')->restore(); + + // Enable Alipay. + $cg = CompanyGateway::where('gateway_key', 'd14dd26a37cecc30fdd65700bfb55b23')->firstOrFail(); + $fees_and_limits = $cg->fees_and_limits; + $fees_and_limits->{GatewayType::ALIPAY} = new FeesAndLimits(); + $cg->fees_and_limits = $fees_and_limits; + $cg->save(); + + // Setting country to DEU (276). + $client = Client::first(); + $client->country_id = 276; + $client->save(); + } + + public function testPayingWithAlipay() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.invoices.index') + ->click('@pay-now') + ->press('Pay Now') + ->clickLink('Alipay') + ->press('Pay Now') + ->waitForText('Alipay test payment page', 120) + ->press('.common-Button.common-Button--default') + ->waitForText('Details of the payment', 60); + }); + } +} diff --git a/tests/Browser/ClientPortal/Gateways/Stripe/CreditCardTest.php b/tests/Browser/ClientPortal/Gateways/Stripe/CreditCardTest.php new file mode 100644 index 000000000000..e15ea3d678e8 --- /dev/null +++ b/tests/Browser/ClientPortal/Gateways/Stripe/CreditCardTest.php @@ -0,0 +1,139 @@ +driver->manage()->deleteAllCookies(); + } + + $this->browse(function (Browser $browser) { + $browser + ->visit(new Login()) + ->auth(); + }); + + $this->disableCompanyGateways(); + + // Enable Stripe. + CompanyGateway::where('gateway_key', 'd14dd26a37cecc30fdd65700bfb55b23')->restore(); + + $cg = CompanyGateway::where('gateway_key', 'd14dd26a37cecc30fdd65700bfb55b23')->firstOrFail(); + $fees_and_limits = $cg->fees_and_limits; + $fees_and_limits->{GatewayType::CREDIT_CARD} = new FeesAndLimits(); + $cg->fees_and_limits = $fees_and_limits; + $cg->save(); + } + + public function testPaymentWithNewCard() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.invoices.index') + ->click('@pay-now') + ->click('@pay-now-dropdown') + ->clickLink('Credit Card') + ->type('#cardholder-name', 'John Doe') + ->withinFrame('iframe', function (Browser $browser) { + $browser + ->type('cardnumber', '4242 4242 4242 4242') + ->type('exp-date', '04/22') + ->type('cvc', '242'); + }) + ->click('#pay-now') + ->waitForText('Details of the payment', 60); + }); + } + + public function testPayWithNewCardAndSaveForFutureUse() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.invoices.index') + ->click('@pay-now') + ->click('@pay-now-dropdown') + ->clickLink('Credit Card') + ->type('#cardholder-name', 'John Doe') + ->withinFrame('iframe', function (Browser $browser) { + $browser + ->type('cardnumber', '4242 4242 4242 4242') + ->type('exp-date', '04/22') + ->type('cvc', '242'); + }) + ->radio('#proxy_is_default', true) + ->click('#pay-now') + ->waitForText('Details of the payment', 60) + ->visitRoute('client.payment_methods.index') + ->clickLink('View') + ->assertSee('4242'); + }); + } + + public function testPayWithSavedCreditCard() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.invoices.index') + ->click('@pay-now') + ->click('@pay-now-dropdown') + ->clickLink('Credit Card') + ->click('.toggle-payment-with-token') + ->click('#pay-now') + ->waitForText('Details of the payment', 60); + }); + } + + public function testRemoveCreditCard() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.payment_methods.index') + ->clickLink('View') + ->press('Remove Payment Method') + ->waitForText('Confirmation') + ->click('@confirm-payment-removal') + ->assertSee('Payment method has been successfully removed.'); + }); + } + + public function testAddingCreditCardStandalone() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.payment_methods.index') + ->press('Add Payment Method') + ->clickLink('Credit Card') + ->type('#cardholder-name', 'John Doe') + ->withinFrame('iframe', function (Browser $browser) { + $browser + ->type('cardnumber', '4242 4242 4242 4242') + ->type('exp-date', '04/22') + ->type('cvc', '242'); + }) + ->press('Add Payment Method') + ->waitForText('**** 4242'); + }); + } +} diff --git a/tests/Browser/ClientPortal/Gateways/Stripe/SofortTest.php b/tests/Browser/ClientPortal/Gateways/Stripe/SofortTest.php new file mode 100644 index 000000000000..a8a0fe88d548 --- /dev/null +++ b/tests/Browser/ClientPortal/Gateways/Stripe/SofortTest.php @@ -0,0 +1,72 @@ +driver->manage()->deleteAllCookies(); + } + + $this->browse(function (Browser $browser) { + $browser + ->visit(new Login()) + ->auth(); + }); + + $this->disableCompanyGateways(); + + // Enable Stripe. + CompanyGateway::where('gateway_key', 'd14dd26a37cecc30fdd65700bfb55b23')->restore(); + + // Enable SOFORT. + $cg = CompanyGateway::where('gateway_key', 'd14dd26a37cecc30fdd65700bfb55b23')->firstOrFail(); + $fees_and_limits = $cg->fees_and_limits; + $fees_and_limits->{GatewayType::SOFORT} = new FeesAndLimits(); + $cg->fees_and_limits = $fees_and_limits; + $cg->save(); + + // SOFORT required ['AUT', 'BEL', 'DEU', 'ITA', 'NLD', 'ESP'] to be billing country. + // Setting country to DEU (276). + $client = Client::first(); + $client->country_id = 276; + $client->save(); + } + + public function testPayingWithSofort() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.invoices.index') + ->click('@pay-now') + ->press('Pay Now') + ->clickLink('Sofort') + ->press('Pay Now') + ->waitForText('Sofort test payment page', 120) + ->press('.common-Button.common-Button--default') + ->waitForText('Details of the payment', 60); + }); + } +} diff --git a/tests/Browser/ClientPortal/Gateways/WePay/ACHTest.php b/tests/Browser/ClientPortal/Gateways/WePay/ACHTest.php new file mode 100644 index 000000000000..25e405429747 --- /dev/null +++ b/tests/Browser/ClientPortal/Gateways/WePay/ACHTest.php @@ -0,0 +1,62 @@ +driver->manage()->deleteAllCookies(); + } + + $this->disableCompanyGateways(); + + CompanyGateway::where('gateway_key', '8fdeed552015b3c7b44ed6c8ebd9e992')->restore(); + + // Enable ACH. + $cg = CompanyGateway::where('gateway_key', '8fdeed552015b3c7b44ed6c8ebd9e992')->firstOrFail(); + $fees_and_limits = $cg->fees_and_limits; + $fees_and_limits->{GatewayType::BANK_TRANSFER} = new FeesAndLimits(); + + $cg->fees_and_limits = $fees_and_limits; + $cg->save(); + + $this->browse(function (Browser $browser) { + $browser + ->visit(new Login()) + ->auth(); + }); + } + + public function testPayingWithNoPreauthorizedIsntPossible() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.invoices.index') + ->click('@pay-now') + ->press('Pay Now') + ->clickLink('Bank Transfer') + ->assertSee('To pay with a bank account, first you have to add it as payment method.'); + }); + } +} \ No newline at end of file diff --git a/tests/Browser/ClientPortal/Gateways/WePay/CreditCardTest.php b/tests/Browser/ClientPortal/Gateways/WePay/CreditCardTest.php new file mode 100644 index 000000000000..062c1b411549 --- /dev/null +++ b/tests/Browser/ClientPortal/Gateways/WePay/CreditCardTest.php @@ -0,0 +1,123 @@ +driver->manage()->deleteAllCookies(); + } + + $this->disableCompanyGateways(); + + CompanyGateway::where('gateway_key', '8fdeed552015b3c7b44ed6c8ebd9e992')->restore(); + + $this->browse(function (Browser $browser) { + $browser + ->visit(new Login()) + ->auth(); + }); + } + + public function testPayWithNewCard() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.invoices.index') + ->click('@pay-now') + ->press('Pay Now') + ->clickLink('Credit Card') + ->type('card-number', '4003830171874018') + ->type('card-holders-name', 'John Doe') + ->type('.expiry', '12/28') + ->type('cvc', '100') + ->press('Pay Now') + ->waitForText('Details of the payment', 60); + }); + } + + public function testPayWithNewCardAndSaveForFutureUse() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.invoices.index') + ->click('@pay-now') + ->press('Pay Now') + ->clickLink('Credit Card') + ->type('card-number', '4003830171874018') + ->type('card-holders-name', 'John Doe') + ->type('.expiry', '12/28') + ->type('cvc', '100') + ->radio('#proxy_is_default', true) + ->press('Pay Now') + ->waitForText('Details of the payment', 60) + ->visitRoute('client.payment_methods.index') + ->clickLink('View') + ->assertSee('4018'); + }); + } + + public function testPayWithSavedCreditCard() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.invoices.index') + ->click('@pay-now') + ->press('Pay Now') + ->clickLink('Credit Card') + ->click('.toggle-payment-with-token') + ->press('Pay Now') + ->waitForText('Details of the payment', 60); + }); + } + + public function testRemoveCreditCard() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.payment_methods.index') + ->clickLink('View') + ->press('Remove Payment Method') + ->waitForText('Confirmation') + ->click('@confirm-payment-removal') + ->assertSee('Payment method has been successfully removed.'); + }); + } + + public function testAddingCreditCardStandalone() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.payment_methods.index') + ->press('Add Payment Method') + ->clickLink('Credit Card') + ->waitForText('Credit Card') + ->type('#cardholder_name', 'John Doe') + ->type('card-number', '4003830171874018') + ->type('card-holders-name', 'John Doe') + ->type('.expiry', '12/28') + ->type('cvc', '100') + ->press('Add Payment Method') + ->waitForText(4018, 60); + }); + } +} diff --git a/tests/Browser/ClientPortal/InvoicesTest.php b/tests/Browser/ClientPortal/InvoicesTest.php new file mode 100644 index 000000000000..838ae1bc6671 --- /dev/null +++ b/tests/Browser/ClientPortal/InvoicesTest.php @@ -0,0 +1,79 @@ +driver->manage()->deleteAllCookies(); + } + + $this->browse(function (Browser $browser) { + $browser + ->visit(new Login()) + ->auth(); + }); + } + + public function testPageLoads() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.invoices.index') + ->assertSeeIn('span[data-ref="meta-title"]', 'Invoices') + ->visitRoute('client.logout'); + }); + } + + public function testClickingPayNowWithoutInvoices() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.invoices.index') + ->press('Pay Now') + ->assertSee('No payable invoices selected. Make sure you are not trying to pay draft invoice or invoice with zero balance due.') + ->visitRoute('client.logout'); + }); + } + + public function testClickingDownloadWithoutInvoices() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.invoices.index') + ->press('Download') + ->assertSee('No items selected.') + ->visitRoute('client.logout'); + }); + } + + public function testCheckingInvoiceAndClickingPayNow() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.invoices.index') + ->check('.form-check.form-check-child') + ->press('Pay Now') + ->assertPathIs('/client/invoices/payment') + ->visitRoute('client.logout'); + }); + } +} diff --git a/tests/Browser/ClientPortal/LoginTest.php b/tests/Browser/ClientPortal/LoginTest.php new file mode 100644 index 000000000000..407e2f3c2999 --- /dev/null +++ b/tests/Browser/ClientPortal/LoginTest.php @@ -0,0 +1,65 @@ +driver->manage()->deleteAllCookies(); + } + } + + public function testLoginPage() + { + $this->browse(function (Browser $browser) { + $browser + ->visit(route('client.login')) + ->assertSee('Client Portal') + ->type('email', 'user@example.com') + ->type('password', 'password') + ->press('Login'); + + $browser->assertPathIs('/client/invoices'); + }); + } + + public function testLoginFormValidation() + { + $this->browse(function (Browser $browser) { + $browser + ->visit(route('client.login')) + ->press('Login') + ->assertSee('The email field is required.') + ->assertSee('The password field is required.'); + }); + } + + public function testForgotPasswordLink() + { + $this->browse(function (Browser $browser) { + $browser + ->visit(route('client.login')) + ->assertSeeLink('Forgot your password?') + ->clickLink('Forgot your password?') + ->assertPathIs('/client/password/reset'); + }); + } +} diff --git a/tests/Browser/ClientPortal/PaymentMethodsTest.php b/tests/Browser/ClientPortal/PaymentMethodsTest.php new file mode 100644 index 000000000000..56feb97f80c2 --- /dev/null +++ b/tests/Browser/ClientPortal/PaymentMethodsTest.php @@ -0,0 +1,45 @@ +driver->manage()->deleteAllCookies(); + } + + $this->browse(function (Browser $browser) { + $browser + ->visit(new Login()) + ->auth(); + }); + } + + public function testPageLoads() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.payment_methods.index') + ->assertSeeIn('span[data-ref="meta-title"]', 'Payment Methods') + ->visitRoute('client.logout'); + }); + } +} diff --git a/tests/Browser/ClientPortal/PaymentsTest.php b/tests/Browser/ClientPortal/PaymentsTest.php new file mode 100644 index 000000000000..835389d16506 --- /dev/null +++ b/tests/Browser/ClientPortal/PaymentsTest.php @@ -0,0 +1,45 @@ +driver->manage()->deleteAllCookies(); + } + + $this->browse(function (Browser $browser) { + $browser + ->visit(new Login()) + ->auth(); + }); + } + + public function testPageLoads() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.payments.index') + ->assertSeeIn('span[data-ref="meta-title"]', 'Payments') + ->visitRoute('client.logout'); + }); + } +} diff --git a/tests/Browser/ClientPortal/ProfileSettingsTest.php b/tests/Browser/ClientPortal/ProfileSettingsTest.php new file mode 100644 index 000000000000..ff567c60dfbb --- /dev/null +++ b/tests/Browser/ClientPortal/ProfileSettingsTest.php @@ -0,0 +1,210 @@ +faker = Factory::create(); + + foreach (static::$browsers as $browser) { + $browser->driver->manage()->deleteAllCookies(); + } + + $this->browse(function (Browser $browser) { + $browser + ->visit(new Login()) + ->auth(); + }); + } + + public function testPageLoads() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.invoices.index') + ->click('button[data-ref="client-profile-dropdown"]') + ->click('a[data-ref="client-profile-dropdown-settings"]') + ->waitForText('Client Information') + ->assertSeeIn('span[data-ref="meta-title"]', 'Client Information') + ->visitRoute('client.logout'); + }); + } + + public function testClientDetailsUpdate() + { + $original = [ + 'name' => $this->faker->name, + 'vat_number' => (string)$this->faker->randomNumber(6), + 'phone' => $this->faker->phoneNumber, + 'website' => $this->faker->url, + ]; + + $this->browse(function (Browser $browser) use ($original) { + $browser + ->visitRoute('client.invoices.index') + ->click('button[data-ref="client-profile-dropdown"]') + ->click('a[data-ref="client-profile-dropdown-settings"]') + ->waitForText('Client Information'); + + $browser + ->with('#update_contact', function (Browser $form) use ($original) { + $form + ->type('#client_name', $original['name']) + ->type('#client_vat_number', $original['vat_number']) + ->type('#client_phone', $original['phone']) + ->type('#client_website', $original['website']) + ->press('Save'); + }) + ->pause(2000) + ->refresh(); + + $updated = [ + 'name' => $browser->value('#client_name'), + 'vat_number' => $browser->value('#client_vat_number'), + 'phone' => $browser->value('#client_phone'), + 'website' => $browser->value('#client_website') + ]; + + $this->assertSame($original, $updated); + }); + } + + public function testContactDetailsUpdate() + { + $original = [ + 'first_name' => $this->faker->firstName, + 'last_name' => $this->faker->lastName, + 'email_address' => 'user@example.com', + 'phone' => $this->faker->phoneNumber, + ]; + + $this->browse(function (Browser $browser) use ($original) { + $browser + ->visitRoute('client.invoices.index') + ->click('button[data-ref="client-profile-dropdown"]') + ->click('a[data-ref="client-profile-dropdown-settings"]') + ->waitForText('Client Information'); + + $browser + ->with('#update_client', function (Browser $form) use ($original) { + $form + ->type('#contact_first_name', $original['first_name']) + ->type('#contact_last_name', $original['last_name']) + ->scrollIntoView('#contact_email_address') + ->type('#contact_email_address', $original['email_address']) + ->type('#contact_phone', $original['phone']) + ->click('button[data-ref="update-contact-details"]'); + }) + ->pause(2000) + ->refresh(); + + $updated = [ + 'first_name' => $browser->value('#contact_first_name'), + 'last_name' => $browser->value('#contact_last_name'), + 'email_address' => $browser->value('#contact_email_address'), + 'phone' => $browser->value('#contact_phone'), + ]; + + $this->assertSame($original, $updated); + }); + } + + public function testBillingAddressUpdate() + { + $original = [ + 'street' => $this->faker->streetName, + 'apt' => $this->faker->streetAddress, + 'city' => $this->faker->city, + 'state' => $this->faker->state, + 'postal_code' => $this->faker->postcode, + ]; + + $this->browse(function (Browser $browser) use ($original) { + $browser + ->visitRoute('client.invoices.index') + ->click('button[data-ref="client-profile-dropdown"]') + ->click('a[data-ref="client-profile-dropdown-settings"]') + ->waitForText('Client Information'); + + $browser + ->with('#update_billing_address', function (Browser $form) use ($original) { + $form + ->type('#address1', $original['street']) + ->type('#address2', $original['apt']) + ->type('#city', $original['city']) + ->type('#state', $original['state']) + ->type('#postal_code', $original['postal_code']) + ->select('#country') + ->press('Save'); + }) + ->pause(1000) + ->refresh(); + + $updated = [ + 'street' => $browser->value('#address1'), + 'apt' => $browser->value('#address2'), + 'city' => $browser->value('#city'), + 'state' => $browser->value('#state'), + 'postal_code' => $browser->value('#postal_code'), + ]; + + $this->assertSame($original, $updated); + }); + } + + public function testShippingAddressUpdate() + { + $original = [ + 'street' => $this->faker->streetName, + 'apt' => $this->faker->streetAddress, + 'city' => $this->faker->city, + 'state' => $this->faker->state, + 'postal_code' => $this->faker->postcode, + ]; + + $this->browse(function (Browser $browser) use ($original) { + $browser + ->visitRoute('client.invoices.index') + ->click('button[data-ref="client-profile-dropdown"]') + ->click('a[data-ref="client-profile-dropdown-settings"]') + ->waitForText('Client Information'); + + $browser + ->with('#update_shipping_address', function (Browser $form) use ($original) { + $form + ->type('#shipping_address1', $original['street']) + ->type('#shipping_address2', $original['apt']) + ->type('#shipping_city', $original['city']) + ->type('#shipping_state', $original['state']) + ->type('#shipping_postal_code', $original['postal_code']) + ->select('#shipping_country') + ->press('Save'); + }) + ->pause(1000) + ->refresh(); + + $updated = [ + 'street' => $browser->value('#shipping_address1'), + 'apt' => $browser->value('#shipping_address2'), + 'city' => $browser->value('#shipping_city'), + 'state' => $browser->value('#shipping_state'), + 'postal_code' => $browser->value('#shipping_postal_code'), + ]; + + $this->assertSame($original, $updated); + }); + } +} diff --git a/tests/Browser/ClientPortal/QuotesTest.php b/tests/Browser/ClientPortal/QuotesTest.php new file mode 100644 index 000000000000..2a294eca2ed2 --- /dev/null +++ b/tests/Browser/ClientPortal/QuotesTest.php @@ -0,0 +1,84 @@ +driver->manage()->deleteAllCookies(); + } + + $this->browse(function (Browser $browser) { + $browser + ->visit(new Login()) + ->auth(); + }); + } + + public function testPageLoads() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.quotes.index') + ->assertSeeIn('span[data-ref="meta-title"]', 'Quotes') + ->visitRoute('client.logout'); + }); + } + + public function testClickingApproveWithoutQuotesDoesntWork() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.quotes.index') + ->press('Approve') + ->assertPathIs('/client/quotes'); + }); + } + + public function testApprovingQuotes() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.quotes.index') + ->check('.form-check.form-check-child') + ->press('Approve') + ->assertPathIs('/client/quotes/approve') + ->press('Approve') + ->assertPathIs('/client/quotes') + ->assertSee('Quote(s) approved successfully.') + ->visitRoute('client.logout'); + }); + } + + public function testQuotesWithSentStatusCanOnlyBeApproved() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.quotes.index') + ->check('.form-check.form-check-child') + ->press('Approve') + ->assertPathIs('/client/quotes') + ->assertDontSee('Quote(s) approved successfully.') + ->visitRoute('client.logout'); + }); + } +} diff --git a/tests/Browser/ClientPortal/RecurringInvoicesTest.php b/tests/Browser/ClientPortal/RecurringInvoicesTest.php new file mode 100644 index 000000000000..d5d9b711ef6b --- /dev/null +++ b/tests/Browser/ClientPortal/RecurringInvoicesTest.php @@ -0,0 +1,65 @@ +driver->manage()->deleteAllCookies(); + } + + $this->browse(function (Browser $browser) { + $browser + ->visit(new Login()) + ->auth(); + }); + } + + public function testPageLoads() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.recurring_invoices.index') + ->assertSeeIn('span[data-ref="meta-title"]', 'Recurring Invoices') + ->visitRoute('client.logout'); + }); + } + + public function testRequestingCancellation() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.recurring_invoices.index') + ->clickLink('View') + ->assertSee('Cancellation') + ->press('Request Cancellation') + ->pause(1000) + ->waitForText('Request cancellation') + ->press('Confirm') + ->pause(5000) + ->assertPathIs( + route('client.recurring_invoices.request_cancellation', RecurringInvoice::first()->hashed_id, false) + ) + ->visitRoute('client.logout'); + }); + } +} diff --git a/tests/Browser/ClientPortal/SubscriptionsTest.php b/tests/Browser/ClientPortal/SubscriptionsTest.php new file mode 100644 index 000000000000..b0962cc3e333 --- /dev/null +++ b/tests/Browser/ClientPortal/SubscriptionsTest.php @@ -0,0 +1,45 @@ +driver->manage()->deleteAllCookies(); + } + + $this->browse(function (Browser $browser) { + $browser + ->visit(new Login()) + ->auth(); + }); + } + + public function testPageLoads() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.subscriptions.index') + ->assertSeeIn('span[data-ref="meta-title"]', 'Subscriptions') + ->visitRoute('client.logout'); + }); + } +} diff --git a/tests/Browser/Pages/ClientPortal/Login.php b/tests/Browser/Pages/ClientPortal/Login.php new file mode 100644 index 000000000000..11125eba444a --- /dev/null +++ b/tests/Browser/Pages/ClientPortal/Login.php @@ -0,0 +1,51 @@ +assertPathIs($this->url()); + } + + /** + * Get the element shortcuts for the page. + * + * @return array + */ + public function elements() + { + return [ + '@element' => '#selector', + ]; + } + + public function auth(Browser $browser) + { + $browser + ->visitRoute('client.login') + ->type('email', 'user@example.com') + ->type('password', 'password') + ->press('Login'); + } +} diff --git a/tests/Browser/console/.gitignore b/tests/Browser/console/.gitignore new file mode 100644 index 000000000000..d6b7ef32c847 --- /dev/null +++ b/tests/Browser/console/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/tests/Browser/screenshots/.gitignore b/tests/Browser/screenshots/.gitignore new file mode 100644 index 000000000000..d6b7ef32c847 --- /dev/null +++ b/tests/Browser/screenshots/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/tests/DuskTestCase.php b/tests/DuskTestCase.php new file mode 100644 index 000000000000..a7cdfe734de8 --- /dev/null +++ b/tests/DuskTestCase.php @@ -0,0 +1,72 @@ +addArguments(collect([ + '--window-size=1920,1080', + ])->unless($this->hasHeadlessDisabled(), function ($items) { + return $items->merge([ + '--disable-gpu', + '--headless', + ]); + })->all()); + + return RemoteWebDriver::create( + $_ENV['DUSK_DRIVER_URL'] ?? 'http://localhost:9515', + DesiredCapabilities::chrome()->setCapability( + ChromeOptions::CAPABILITY, $options + ) + ); + } + + /** + * Determine whether the Dusk command has disabled headless mode. + * + * @return bool + */ + protected function hasHeadlessDisabled() + { + return isset($_SERVER['DUSK_HEADLESS_DISABLED']) || + isset($_ENV['DUSK_HEADLESS_DISABLED']); + } + + /** + * Disable all company gateways, test classes should enable them per need. + * + * @return void + */ + public function disableCompanyGateways() + { + CompanyGateway::where('company_id', 1)->delete(); + } +} diff --git a/tests/ClientPortal/CreditsTest.php b/tests/Feature/ClientPortal/CreditsTest.php similarity index 98% rename from tests/ClientPortal/CreditsTest.php rename to tests/Feature/ClientPortal/CreditsTest.php index 129c811bc34c..dc61ad687b6f 100644 --- a/tests/ClientPortal/CreditsTest.php +++ b/tests/Feature/ClientPortal/CreditsTest.php @@ -10,7 +10,7 @@ * @license https://www.elastic.co/licensing/elastic-license */ -namespace Tests\ClientPortal; +namespace Tests\Feature\ClientPortal; use App\Http\Livewire\CreditsTable; use App\Models\Account; @@ -23,6 +23,7 @@ use Faker\Factory; use Illuminate\Foundation\Testing\DatabaseTransactions; use Livewire\Livewire; use Tests\TestCase; +use function now; class CreditsTest extends TestCase { diff --git a/tests/Feature/ClientPortal/InvoicesTest.php b/tests/Feature/ClientPortal/InvoicesTest.php new file mode 100644 index 000000000000..2ec29de8ca01 --- /dev/null +++ b/tests/Feature/ClientPortal/InvoicesTest.php @@ -0,0 +1,90 @@ +faker = Factory::create(); + } + + public function testInvoiceTableFilters() + { + $account = Account::factory()->create(); + + $user = User::factory()->create( + ['account_id' => $account->id, 'email' => $this->faker->safeEmail] + ); + + $company = Company::factory()->create(['account_id' => $account->id]); + + $client = Client::factory()->create(['company_id' => $company->id, 'user_id' => $user->id]); + + ClientContact::factory()->count(2)->create([ + 'user_id' => $user->id, + 'client_id' => $client->id, + 'company_id' => $company->id, + ]); + + $sent = Invoice::factory()->create([ + 'user_id' => $user->id, + 'company_id' => $company->id, + 'client_id' => $client->id, + 'status_id' => Invoice::STATUS_SENT, + ]); + + $paid = Invoice::factory()->create([ + 'user_id' => $user->id, + 'company_id' => $company->id, + 'client_id' => $client->id, + 'status_id' => Invoice::STATUS_PAID, + ]); + + $unpaid = Invoice::factory()->create([ + 'user_id' => $user->id, + 'company_id' => $company->id, + 'client_id' => $client->id, + 'status_id' => Invoice::STATUS_UNPAID, + ]); + + $this->actingAs($client->contacts->first(), 'contact'); + + Livewire::test(InvoicesTable::class, ['company' => $company]) + ->assertSee($sent->number) + ->assertSee($paid->number) + ->assertSee($unpaid->number); + + Livewire::test(InvoicesTable::class, ['company' => $company]) + ->set('status', ['paid']) + ->assertSee($paid->number) + ->assertDontSee($unpaid->number); + } +}