diff --git a/src/tests/Browser/WalletTest.php b/src/tests/Browser/WalletTest.php index febfbe0a..b87e9432 100644 --- a/src/tests/Browser/WalletTest.php +++ b/src/tests/Browser/WalletTest.php @@ -1,256 +1,260 @@ deleteTestUser('wallets-controller@kolabnow.com'); $john = $this->getTestUser('john@kolab.org'); Wallet::where('user_id', $john->id)->update(['balance' => -1234]); } /** * {@inheritDoc} */ public function tearDown(): void { $this->deleteTestUser('wallets-controller@kolabnow.com'); $john = $this->getTestUser('john@kolab.org'); Wallet::where('user_id', $john->id)->update(['balance' => 0]); parent::tearDown(); } /** * Test wallet page (unauthenticated) */ public function testWalletUnauth(): void { // Test that the page requires authentication $this->browse(function (Browser $browser) { $browser->visit('/wallet')->on(new Home()); }); } /** * Test wallet "box" on Dashboard */ public function testDashboard(): void { // Test that the page requires authentication $this->browse(function (Browser $browser) { $browser->visit(new Home()) ->submitLogon('john@kolab.org', 'simple123', true) ->on(new Dashboard()) ->assertSeeIn('@links .link-wallet .name', 'Wallet') ->assertSeeIn('@links .link-wallet .badge', '-12,34 CHF'); }); } /** * Test wallet page * * @depends testDashboard */ public function testWallet(): void { $this->browse(function (Browser $browser) { $browser->click('@links .link-wallet') ->on(new WalletPage()) ->assertSeeIn('#wallet .card-title', 'Account balance -12,34 CHF') ->assertSeeIn('#wallet .card-title .text-danger', '-12,34 CHF') ->assertSeeIn('#wallet .card-text', 'You are out of credit'); }); } /** * Test Receipts tab */ public function testReceipts(): void { $user = $this->getTestUser('wallets-controller@kolabnow.com', ['password' => 'simple123']); $wallet = $user->wallets()->first(); $wallet->payments()->delete(); // Log out John and log in the test user $this->browse(function (Browser $browser) { $browser->visit('/logout') ->waitForLocation('/login') ->on(new Home()) ->submitLogon('wallets-controller@kolabnow.com', 'simple123', true); }); // Assert Receipts tab content when there's no receipts available $this->browse(function (Browser $browser) { $browser->on(new Dashboard()) ->click('@links .link-wallet') ->on(new WalletPage()) ->assertSeeIn('#wallet .card-title', 'Account balance 0,00 CHF') ->assertSeeIn('#wallet .card-title .text-success', '0,00 CHF') ->assertSeeIn('#wallet .card-text', 'You are in your free trial period.') ->assertSeeIn('@nav #tab-receipts', 'Receipts') ->with('@receipts-tab', function (Browser $browser) { $browser->waitUntilMissing('.app-loader') ->assertSeeIn('p', 'There are no receipts for payments') ->assertDontSeeIn('p', 'Here you can download') ->assertMissing('select') ->assertMissing('button'); }); }); // Create some sample payments $receipts = []; $date = Carbon::create(intval(date('Y')) - 1, 3, 30); $payment = Payment::create([ 'id' => 'AAA1', 'status' => PaymentProvider::STATUS_PAID, 'type' => PaymentProvider::TYPE_ONEOFF, 'description' => 'Paid in March', 'wallet_id' => $wallet->id, 'provider' => 'stripe', 'amount' => 1111, + 'currency_amount' => 1111, + 'currency' => 'CHF', ]); $payment->updated_at = $date; $payment->save(); $receipts[] = $date->format('Y-m'); $date = Carbon::create(intval(date('Y')) - 1, 4, 30); $payment = Payment::create([ 'id' => 'AAA2', 'status' => PaymentProvider::STATUS_PAID, 'type' => PaymentProvider::TYPE_ONEOFF, 'description' => 'Paid in April', 'wallet_id' => $wallet->id, 'provider' => 'stripe', 'amount' => 1111, + 'currency_amount' => 1111, + 'currency' => 'CHF', ]); $payment->updated_at = $date; $payment->save(); $receipts[] = $date->format('Y-m'); // Assert Receipts tab with receipts available $this->browse(function (Browser $browser) use ($receipts) { $browser->refresh() ->on(new WalletPage()) ->assertSeeIn('@nav #tab-receipts', 'Receipts') ->with('@receipts-tab', function (Browser $browser) use ($receipts) { $browser->waitUntilMissing('.app-loader') ->assertDontSeeIn('p', 'There are no receipts for payments') ->assertSeeIn('p', 'Here you can download') ->assertSeeIn('button', 'Download') ->assertElementsCount('select > option', 2) ->assertSeeIn('select > option:nth-child(1)', $receipts[1]) ->assertSeeIn('select > option:nth-child(2)', $receipts[0]); // Download a receipt file $browser->select('select', $receipts[0]) ->click('button') ->pause(2000); $files = glob(__DIR__ . '/downloads/*.pdf'); $filename = pathinfo($files[0], PATHINFO_BASENAME); $this->assertTrue(strpos($filename, $receipts[0]) !== false); $content = $browser->readDownloadedFile($filename, 0); $this->assertStringStartsWith("%PDF-1.", $content); $browser->removeDownloadedFile($filename); }); }); } /** * Test History tab */ public function testHistory(): void { $user = $this->getTestUser('wallets-controller@kolabnow.com', ['password' => 'simple123']); // Log out John and log in the test user $this->browse(function (Browser $browser) { $browser->visit('/logout') ->waitForLocation('/login') ->on(new Home()) ->submitLogon('wallets-controller@kolabnow.com', 'simple123', true); }); $package_kolab = \App\Package::where('title', 'kolab')->first(); $user->assignPackage($package_kolab); $wallet = $user->wallets()->first(); // Create some sample transactions $transactions = $this->createTestTransactions($wallet); $transactions = array_reverse($transactions); $pages = array_chunk($transactions, 10 /* page size*/); $this->browse(function (Browser $browser) use ($pages) { $browser->on(new Dashboard()) ->click('@links .link-wallet') ->on(new WalletPage()) ->assertSeeIn('@nav #tab-history', 'History') ->click('@nav #tab-history') ->with('@history-tab', function (Browser $browser) use ($pages) { $browser->waitUntilMissing('.app-loader') ->assertElementsCount('table tbody tr', 10) ->assertMissing('table td.email') ->assertSeeIn('#transactions-loader button', 'Load more'); foreach ($pages[0] as $idx => $transaction) { $selector = 'table tbody tr:nth-child(' . ($idx + 1) . ')'; $priceStyle = $transaction->type == Transaction::WALLET_AWARD ? 'text-success' : 'text-danger'; $browser->assertSeeIn("$selector td.description", $transaction->shortDescription()) ->assertMissing("$selector td.selection button") ->assertVisible("$selector td.price.{$priceStyle}"); // TODO: Test more transaction details } // Load the next page $browser->click('#transactions-loader button') ->waitUntilMissing('.app-loader') ->assertElementsCount('table tbody tr', 12) ->assertMissing('#transactions-loader button'); $debitEntry = null; foreach ($pages[1] as $idx => $transaction) { $selector = 'table tbody tr:nth-child(' . ($idx + 1 + 10) . ')'; $priceStyle = $transaction->type == Transaction::WALLET_CREDIT ? 'text-success' : 'text-danger'; $browser->assertSeeIn("$selector td.description", $transaction->shortDescription()); if ($transaction->type == Transaction::WALLET_DEBIT) { $debitEntry = $selector; } else { $browser->assertMissing("$selector td.selection button"); } } // Load sub-transactions $browser->click("$debitEntry td.selection button") ->waitUntilMissing('.app-loader') ->assertElementsCount("$debitEntry td.description ul li", 2) ->assertMissing("$debitEntry td.selection button"); }); }); } } diff --git a/src/tests/Feature/Controller/WalletsTest.php b/src/tests/Feature/Controller/WalletsTest.php index 22cb707a..e327f1a2 100644 --- a/src/tests/Feature/Controller/WalletsTest.php +++ b/src/tests/Feature/Controller/WalletsTest.php @@ -1,351 +1,353 @@ deleteTestUser('wallets-controller@kolabnow.com'); } /** * {@inheritDoc} */ public function tearDown(): void { $this->deleteTestUser('wallets-controller@kolabnow.com'); parent::tearDown(); } /** * Test for getWalletNotice() method */ public function testGetWalletNotice(): void { $user = $this->getTestUser('wallets-controller@kolabnow.com'); $package = \App\Package::where('title', 'kolab')->first(); $user->assignPackage($package); $wallet = $user->wallets()->first(); $controller = new WalletsController(); $method = new \ReflectionMethod($controller, 'getWalletNotice'); $method->setAccessible(true); // User/entitlements created today, balance=0 $notice = $method->invoke($controller, $wallet); $this->assertSame('You are in your free trial period.', $notice); $wallet->owner->created_at = Carbon::now()->subDays(15); $wallet->owner->save(); $notice = $method->invoke($controller, $wallet); $this->assertSame('Your free trial is about to end, top up to continue.', $notice); // User/entitlements created today, balance=-10 CHF $wallet->balance = -1000; $notice = $method->invoke($controller, $wallet); $this->assertSame('You are out of credit, top up your balance now.', $notice); // User/entitlements created slightly more than a month ago, balance=9,99 CHF (monthly) $wallet->owner->created_at = Carbon::now()->subMonthsWithoutOverflow(1)->subDays(1); $wallet->owner->save(); // test "1 month" $wallet->balance = 999; $notice = $method->invoke($controller, $wallet); $this->assertRegExp('/\((1 month|4 weeks)\)/', $notice); // test "2 months" $wallet->balance = 999 * 2.6; $notice = $method->invoke($controller, $wallet); $this->assertRegExp('/\(2 months 2 weeks\)/', $notice); // test "almost 2 years" $wallet->balance = 999 * 23.5; $notice = $method->invoke($controller, $wallet); $this->assertRegExp('/\(1 year 11 months\)/', $notice); // Old entitlements, 100% discount $this->backdateEntitlements($wallet->entitlements, Carbon::now()->subDays(40)); $discount = \App\Discount::where('discount', 100)->first(); $wallet->discount()->associate($discount); $notice = $method->invoke($controller, $wallet->refresh()); $this->assertSame(null, $notice); } /** * Test fetching pdf receipt */ public function testReceiptDownload(): void { $user = $this->getTestUser('wallets-controller@kolabnow.com'); $john = $this->getTestUser('john@kolab.org'); $wallet = $user->wallets()->first(); // Unauth access not allowed $response = $this->get("api/v4/wallets/{$wallet->id}/receipts/2020-05"); $response->assertStatus(401); $response = $this->actingAs($john)->get("api/v4/wallets/{$wallet->id}/receipts/2020-05"); $response->assertStatus(403); // Invalid receipt id (current month) $receiptId = date('Y-m'); $response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/receipts/{$receiptId}"); $response->assertStatus(404); // Invalid receipt id $receiptId = '1000-03'; $response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/receipts/{$receiptId}"); $response->assertStatus(404); // Valid receipt id $year = intval(date('Y')) - 1; $receiptId = "$year-12"; $filename = \config('app.name') . " Receipt for $year-12"; $response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/receipts/{$receiptId}"); $response->assertStatus(200); $response->assertHeader('content-type', 'application/pdf'); $response->assertHeader('content-disposition', 'attachment; filename="' . $filename . '"'); $response->assertHeader('content-length'); $length = $response->headers->get('content-length'); $content = $response->content(); $this->assertStringStartsWith("%PDF-1.", $content); $this->assertEquals(strlen($content), $length); } /** * Test fetching list of receipts */ public function testReceipts(): void { $user = $this->getTestUser('wallets-controller@kolabnow.com'); $john = $this->getTestUser('john@kolab.org'); $wallet = $user->wallets()->first(); $wallet->payments()->delete(); // Unauth access not allowed $response = $this->get("api/v4/wallets/{$wallet->id}/receipts"); $response->assertStatus(401); $response = $this->actingAs($john)->get("api/v4/wallets/{$wallet->id}/receipts"); $response->assertStatus(403); // Empty list expected $response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/receipts"); $response->assertStatus(200); $json = $response->json(); $this->assertCount(5, $json); $this->assertSame('success', $json['status']); $this->assertSame([], $json['list']); $this->assertSame(1, $json['page']); $this->assertSame(0, $json['count']); $this->assertSame(false, $json['hasMore']); // Insert a payment to the database $date = Carbon::create(intval(date('Y')) - 1, 4, 30); $payment = Payment::create([ 'id' => 'AAA1', 'status' => PaymentProvider::STATUS_PAID, 'type' => PaymentProvider::TYPE_ONEOFF, 'description' => 'Paid in April', 'wallet_id' => $wallet->id, 'provider' => 'stripe', 'amount' => 1111, + 'currency' => 'CHF', + 'currency_amount' => 1111, ]); $payment->updated_at = $date; $payment->save(); $response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/receipts"); $response->assertStatus(200); $json = $response->json(); $this->assertCount(5, $json); $this->assertSame('success', $json['status']); $this->assertSame([$date->format('Y-m')], $json['list']); $this->assertSame(1, $json['page']); $this->assertSame(1, $json['count']); $this->assertSame(false, $json['hasMore']); } /** * Test fetching a wallet (GET /api/v4/wallets/:id) */ public function testShow(): void { $john = $this->getTestUser('john@kolab.org'); $jack = $this->getTestUser('jack@kolab.org'); $wallet = $john->wallets()->first(); $wallet->balance = -100; $wallet->save(); // Accessing a wallet of someone else $response = $this->actingAs($jack)->get("api/v4/wallets/{$wallet->id}"); $response->assertStatus(403); // Accessing non-existing wallet $response = $this->actingAs($jack)->get("api/v4/wallets/aaa"); $response->assertStatus(404); // Wallet owner $response = $this->actingAs($john)->get("api/v4/wallets/{$wallet->id}"); $response->assertStatus(200); $json = $response->json(); $this->assertSame($wallet->id, $json['id']); $this->assertSame('CHF', $json['currency']); $this->assertSame($wallet->balance, $json['balance']); $this->assertTrue(empty($json['description'])); $this->assertTrue(!empty($json['notice'])); } /** * Test fetching wallet transactions */ public function testTransactions(): void { $package_kolab = \App\Package::where('title', 'kolab')->first(); $user = $this->getTestUser('wallets-controller@kolabnow.com'); $user->assignPackage($package_kolab); $john = $this->getTestUser('john@kolab.org'); $wallet = $user->wallets()->first(); // Unauth access not allowed $response = $this->get("api/v4/wallets/{$wallet->id}/transactions"); $response->assertStatus(401); $response = $this->actingAs($john)->get("api/v4/wallets/{$wallet->id}/transactions"); $response->assertStatus(403); // Expect empty list $response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/transactions"); $response->assertStatus(200); $json = $response->json(); $this->assertCount(5, $json); $this->assertSame('success', $json['status']); $this->assertSame([], $json['list']); $this->assertSame(1, $json['page']); $this->assertSame(0, $json['count']); $this->assertSame(false, $json['hasMore']); // Create some sample transactions $transactions = $this->createTestTransactions($wallet); $transactions = array_reverse($transactions); $pages = array_chunk($transactions, 10 /* page size*/); // Get the first page $response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/transactions"); $response->assertStatus(200); $json = $response->json(); $this->assertCount(5, $json); $this->assertSame('success', $json['status']); $this->assertSame(1, $json['page']); $this->assertSame(10, $json['count']); $this->assertSame(true, $json['hasMore']); $this->assertCount(10, $json['list']); foreach ($pages[0] as $idx => $transaction) { $this->assertSame($transaction->id, $json['list'][$idx]['id']); $this->assertSame($transaction->type, $json['list'][$idx]['type']); $this->assertSame($transaction->shortDescription(), $json['list'][$idx]['description']); $this->assertFalse($json['list'][$idx]['hasDetails']); $this->assertFalse(array_key_exists('user', $json['list'][$idx])); } $search = null; // Get the second page $response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/transactions?page=2"); $response->assertStatus(200); $json = $response->json(); $this->assertCount(5, $json); $this->assertSame('success', $json['status']); $this->assertSame(2, $json['page']); $this->assertSame(2, $json['count']); $this->assertSame(false, $json['hasMore']); $this->assertCount(2, $json['list']); foreach ($pages[1] as $idx => $transaction) { $this->assertSame($transaction->id, $json['list'][$idx]['id']); $this->assertSame($transaction->type, $json['list'][$idx]['type']); $this->assertSame($transaction->shortDescription(), $json['list'][$idx]['description']); $this->assertSame( $transaction->type == Transaction::WALLET_DEBIT, $json['list'][$idx]['hasDetails'] ); $this->assertFalse(array_key_exists('user', $json['list'][$idx])); if ($transaction->type == Transaction::WALLET_DEBIT) { $search = $transaction->id; } } // Get a non-existing page $response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/transactions?page=3"); $response->assertStatus(200); $json = $response->json(); $this->assertCount(5, $json); $this->assertSame('success', $json['status']); $this->assertSame(3, $json['page']); $this->assertSame(0, $json['count']); $this->assertSame(false, $json['hasMore']); $this->assertCount(0, $json['list']); // Sub-transaction searching $response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/transactions?transaction=123"); $response->assertStatus(404); $response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/transactions?transaction={$search}"); $response->assertStatus(200); $json = $response->json(); $this->assertCount(5, $json); $this->assertSame('success', $json['status']); $this->assertSame(1, $json['page']); $this->assertSame(2, $json['count']); $this->assertSame(false, $json['hasMore']); $this->assertCount(2, $json['list']); $this->assertSame(Transaction::ENTITLEMENT_BILLED, $json['list'][0]['type']); $this->assertSame(Transaction::ENTITLEMENT_BILLED, $json['list'][1]['type']); // Test that John gets 404 if he tries to access // someone else's transaction ID on his wallet's endpoint $wallet = $john->wallets()->first(); $response = $this->actingAs($john)->get("api/v4/wallets/{$wallet->id}/transactions?transaction={$search}"); $response->assertStatus(404); } } diff --git a/src/tests/Feature/Documents/ReceiptTest.php b/src/tests/Feature/Documents/ReceiptTest.php index e0fc5726..356151bb 100644 --- a/src/tests/Feature/Documents/ReceiptTest.php +++ b/src/tests/Feature/Documents/ReceiptTest.php @@ -1,367 +1,385 @@ paymentIDs)->delete(); } /** * {@inheritDoc} */ public function tearDown(): void { $this->deleteTestUser('receipt-test@kolabnow.com'); Payment::whereIn('id', $this->paymentIDs)->delete(); parent::tearDown(); } /** * Test receipt HTML output (without VAT) */ public function testHtmlOutput(): void { $appName = \config('app.name'); $wallet = $this->getTestData(); $receipt = new Receipt($wallet, 2020, 5); $html = $receipt->htmlOutput(); $this->assertStringStartsWith('', $html); $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->loadHTML($html); // Title $title = $dom->getElementById('title'); $this->assertSame("Receipt for May 2020", $title->textContent); // Company name/address $header = $dom->getElementById('header'); $companyOutput = $this->getNodeContent($header->getElementsByTagName('td')[0]); $companyExpected = \config('app.company.name') . "\n" . \config('app.company.address'); $this->assertSame($companyExpected, $companyOutput); // The main table content $content = $dom->getElementById('content'); $records = $content->getElementsByTagName('tr'); $this->assertCount(7, $records); $headerCells = $records[0]->getElementsByTagName('th'); $this->assertCount(3, $headerCells); $this->assertSame('Date', $this->getNodeContent($headerCells[0])); $this->assertSame('Description', $this->getNodeContent($headerCells[1])); $this->assertSame('Amount', $this->getNodeContent($headerCells[2])); $cells = $records[1]->getElementsByTagName('td'); $this->assertCount(3, $cells); $this->assertSame('2020-05-01', $this->getNodeContent($cells[0])); $this->assertSame("$appName Services", $this->getNodeContent($cells[1])); $this->assertSame('12,34 CHF', $this->getNodeContent($cells[2])); $cells = $records[2]->getElementsByTagName('td'); $this->assertCount(3, $cells); $this->assertSame('2020-05-10', $this->getNodeContent($cells[0])); $this->assertSame("$appName Services", $this->getNodeContent($cells[1])); $this->assertSame('0,01 CHF', $this->getNodeContent($cells[2])); $cells = $records[3]->getElementsByTagName('td'); $this->assertCount(3, $cells); $this->assertSame('2020-05-21', $this->getNodeContent($cells[0])); $this->assertSame("$appName Services", $this->getNodeContent($cells[1])); $this->assertSame('1,00 CHF', $this->getNodeContent($cells[2])); $cells = $records[4]->getElementsByTagName('td'); $this->assertCount(3, $cells); $this->assertSame('2020-05-30', $this->getNodeContent($cells[0])); $this->assertSame("Refund", $this->getNodeContent($cells[1])); $this->assertSame('-1,00 CHF', $this->getNodeContent($cells[2])); $cells = $records[5]->getElementsByTagName('td'); $this->assertCount(3, $cells); $this->assertSame('2020-05-31', $this->getNodeContent($cells[0])); $this->assertSame("Chargeback", $this->getNodeContent($cells[1])); $this->assertSame('-0,10 CHF', $this->getNodeContent($cells[2])); $summaryCells = $records[6]->getElementsByTagName('td'); $this->assertCount(2, $summaryCells); $this->assertSame('Total', $this->getNodeContent($summaryCells[0])); $this->assertSame('12,25 CHF', $this->getNodeContent($summaryCells[1])); // Customer data $customer = $dom->getElementById('customer'); $customerCells = $customer->getElementsByTagName('td'); $customerOutput = $this->getNodeContent($customerCells[0]); $customerExpected = "Firstname Lastname\nTest Unicode Straße 150\n10115 Berlin"; $this->assertSame($customerExpected, $this->getNodeContent($customerCells[0])); $customerIdents = $this->getNodeContent($customerCells[1]); //$this->assertTrue(strpos($customerIdents, "Account ID {$wallet->id}") !== false); $this->assertTrue(strpos($customerIdents, "Customer No. {$wallet->owner->id}") !== false); // Company details in the footer $footer = $dom->getElementById('footer'); $footerOutput = $footer->textContent; $this->assertStringStartsWith(\config('app.company.details'), $footerOutput); $this->assertTrue(strpos($footerOutput, \config('app.company.email')) !== false); } /** * Test receipt HTML output (with VAT) */ public function testHtmlOutputVat(): void { \config(['app.vat.rate' => 7.7]); \config(['app.vat.countries' => 'ch']); $appName = \config('app.name'); $wallet = $this->getTestData('CH'); $receipt = new Receipt($wallet, 2020, 5); $html = $receipt->htmlOutput(); $this->assertStringStartsWith('', $html); $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->loadHTML($html); // The main table content $content = $dom->getElementById('content'); $records = $content->getElementsByTagName('tr'); $this->assertCount(9, $records); $cells = $records[1]->getElementsByTagName('td'); $this->assertCount(3, $cells); $this->assertSame('2020-05-01', $this->getNodeContent($cells[0])); $this->assertSame("$appName Services", $this->getNodeContent($cells[1])); $this->assertSame('11,39 CHF', $this->getNodeContent($cells[2])); $cells = $records[2]->getElementsByTagName('td'); $this->assertCount(3, $cells); $this->assertSame('2020-05-10', $this->getNodeContent($cells[0])); $this->assertSame("$appName Services", $this->getNodeContent($cells[1])); $this->assertSame('0,01 CHF', $this->getNodeContent($cells[2])); $cells = $records[3]->getElementsByTagName('td'); $this->assertCount(3, $cells); $this->assertSame('2020-05-21', $this->getNodeContent($cells[0])); $this->assertSame("$appName Services", $this->getNodeContent($cells[1])); $this->assertSame('0,92 CHF', $this->getNodeContent($cells[2])); $cells = $records[4]->getElementsByTagName('td'); $this->assertCount(3, $cells); $this->assertSame('2020-05-30', $this->getNodeContent($cells[0])); $this->assertSame("Refund", $this->getNodeContent($cells[1])); $this->assertSame('-0,92 CHF', $this->getNodeContent($cells[2])); $cells = $records[5]->getElementsByTagName('td'); $this->assertCount(3, $cells); $this->assertSame('2020-05-31', $this->getNodeContent($cells[0])); $this->assertSame("Chargeback", $this->getNodeContent($cells[1])); $this->assertSame('-0,09 CHF', $this->getNodeContent($cells[2])); $subtotalCells = $records[6]->getElementsByTagName('td'); $this->assertCount(2, $subtotalCells); $this->assertSame('Subtotal', $this->getNodeContent($subtotalCells[0])); $this->assertSame('11,31 CHF', $this->getNodeContent($subtotalCells[1])); $vatCells = $records[7]->getElementsByTagName('td'); $this->assertCount(2, $vatCells); $this->assertSame('VAT (7.7%)', $this->getNodeContent($vatCells[0])); $this->assertSame('0,94 CHF', $this->getNodeContent($vatCells[1])); $totalCells = $records[8]->getElementsByTagName('td'); $this->assertCount(2, $totalCells); $this->assertSame('Total', $this->getNodeContent($totalCells[0])); $this->assertSame('12,25 CHF', $this->getNodeContent($totalCells[1])); } /** * Test receipt PDF output */ public function testPdfOutput(): void { $wallet = $this->getTestData(); $receipt = new Receipt($wallet, 2020, 5); $pdf = $receipt->PdfOutput(); $this->assertStringStartsWith("%PDF-1.", $pdf); $this->assertTrue(strlen($pdf) > 5000); // TODO: Test the content somehow } /** * Prepare data for a test * * @param string $country User country code * * @return \App\Wallet */ protected function getTestData(string $country = null): Wallet { Bus::fake(); $user = $this->getTestUser('receipt-test@kolabnow.com'); $user->setSettings([ 'first_name' => 'Firstname', 'last_name' => 'Lastname', 'billing_address' => "Test Unicode Straße 150\n10115 Berlin", 'country' => $country ]); $wallet = $user->wallets()->first(); // Create two payments out of the 2020-05 period // and three in it, plus one in the period but unpaid, // and one with amount 0, and an extra refund and chanrgeback $payment = Payment::create([ 'id' => 'AAA1', 'status' => PaymentProvider::STATUS_PAID, 'type' => PaymentProvider::TYPE_ONEOFF, 'description' => 'Paid in April', 'wallet_id' => $wallet->id, 'provider' => 'stripe', 'amount' => 1111, + 'currency' => 'CHF', + 'currency_amount' => 1111, ]); $payment->updated_at = Carbon::create(2020, 4, 30, 12, 0, 0); $payment->save(); $payment = Payment::create([ 'id' => 'AAA2', 'status' => PaymentProvider::STATUS_PAID, 'type' => PaymentProvider::TYPE_ONEOFF, 'description' => 'Paid in June', 'wallet_id' => $wallet->id, 'provider' => 'stripe', 'amount' => 2222, + 'currency' => 'CHF', + 'currency_amount' => 2222, ]); $payment->updated_at = Carbon::create(2020, 6, 1, 0, 0, 0); $payment->save(); $payment = Payment::create([ 'id' => 'AAA3', 'status' => PaymentProvider::STATUS_PAID, 'type' => PaymentProvider::TYPE_ONEOFF, 'description' => 'Auto-Payment Setup', 'wallet_id' => $wallet->id, 'provider' => 'stripe', 'amount' => 0, + 'currency' => 'CHF', + 'currency_amount' => 0, ]); $payment->updated_at = Carbon::create(2020, 5, 1, 0, 0, 0); $payment->save(); $payment = Payment::create([ 'id' => 'AAA4', 'status' => PaymentProvider::STATUS_OPEN, 'type' => PaymentProvider::TYPE_ONEOFF, 'description' => 'Payment not yet paid', 'wallet_id' => $wallet->id, 'provider' => 'stripe', 'amount' => 999, + 'currency' => 'CHF', + 'currency_amount' => 999, ]); $payment->updated_at = Carbon::create(2020, 5, 1, 0, 0, 0); $payment->save(); // ... so we expect the five three on the receipt $payment = Payment::create([ 'id' => 'AAA5', 'status' => PaymentProvider::STATUS_PAID, 'type' => PaymentProvider::TYPE_ONEOFF, 'description' => 'Payment OK', 'wallet_id' => $wallet->id, 'provider' => 'stripe', 'amount' => 1234, + 'currency' => 'CHF', + 'currency_amount' => 1234, ]); $payment->updated_at = Carbon::create(2020, 5, 1, 0, 0, 0); $payment->save(); $payment = Payment::create([ 'id' => 'AAA6', 'status' => PaymentProvider::STATUS_PAID, 'type' => PaymentProvider::TYPE_ONEOFF, 'description' => 'Payment OK', 'wallet_id' => $wallet->id, 'provider' => 'stripe', 'amount' => 1, + 'currency' => 'CHF', + 'currency_amount' => 1, ]); $payment->updated_at = Carbon::create(2020, 5, 10, 0, 0, 0); $payment->save(); $payment = Payment::create([ 'id' => 'AAA7', 'status' => PaymentProvider::STATUS_PAID, 'type' => PaymentProvider::TYPE_RECURRING, 'description' => 'Payment OK', 'wallet_id' => $wallet->id, 'provider' => 'stripe', 'amount' => 100, + 'currency' => 'CHF', + 'currency_amount' => 100, ]); $payment->updated_at = Carbon::create(2020, 5, 21, 23, 59, 0); $payment->save(); $payment = Payment::create([ 'id' => 'ref1', 'status' => PaymentProvider::STATUS_PAID, 'type' => PaymentProvider::TYPE_REFUND, 'description' => 'refund desc', 'wallet_id' => $wallet->id, 'provider' => 'stripe', 'amount' => -100, + 'currency' => 'CHF', + 'currency_amount' => -100, ]); $payment->updated_at = Carbon::create(2020, 5, 30, 23, 59, 0); $payment->save(); $payment = Payment::create([ 'id' => 'chback1', 'status' => PaymentProvider::STATUS_PAID, 'type' => PaymentProvider::TYPE_CHARGEBACK, 'description' => '', 'wallet_id' => $wallet->id, 'provider' => 'stripe', 'amount' => -10, + 'currency' => 'CHF', + 'currency_amount' => -10, ]); $payment->updated_at = Carbon::create(2020, 5, 31, 23, 59, 0); $payment->save(); // Make sure some config is set so we can test it's put into the receipt if (empty(\config('app.company.name'))) { \config(['app.company.name' => 'Company Co.']); } if (empty(\config('app.company.email'))) { \config(['app.company.email' => 'email@domina.tld']); } if (empty(\config('app.company.details'))) { \config(['app.company.details' => 'VAT No. 123456789']); } if (empty(\config('app.company.address'))) { \config(['app.company.address' => "Test Street 12\n12345 Some Place"]); } return $wallet; } /** * Extract text from a HTML element replacing
with \n * * @param \DOMElement $node The HTML element * * @return string The content */ protected function getNodeContent(\DOMElement $node) { $content = []; foreach ($node->childNodes as $child) { if ($child->nodeName == 'br') { $content[] = "\n"; } else { $content[] = $child->textContent; } } return trim(implode($content)); } }