'admin-'.uniqid('', true).'@example.com', 'password' => bcrypt('secret'), 'admin' => $admin, 'lang' => 'de', 'active' => 1, 'blocked' => 0, ]); } /** * @param array $params */ function newsletterRequest(User $user, array $params = []): NewsletterListFilterRequest { $request = NewsletterListFilterRequest::create('/admin/newsletter-lists/datatable', 'GET', $params); $request->setUserResolver(fn () => $user); $request->setContainer(app())->setRedirector(app(Redirector::class)); $request->validateResolved(); app()->instance('request', $request); return $request; } function newsletterController(): NewsletterListController { return new NewsletterListController(app(NewsletterListService::class)); } /** * @param array $attributes */ function newsletterListCustomer(array $attributes = []): ShoppingUser { return ShoppingUser::forceCreate(array_merge([ 'auth_user_id' => 0, 'billing_email' => 'kunde-'.uniqid('', true).'@example.com', 'billing_firstname' => 'Erika', 'billing_lastname' => 'Muster', 'billing_salutation' => 'ms', 'billing_address' => 'Musterweg 1', 'billing_zipcode' => '12345', 'billing_city' => 'Musterstadt', 'billing_country_id' => 1, 'shipping_country_id' => 1, 'language' => 'de', 'subscribed' => 1, 'has_buyed' => 1, 'faker_mail' => 0, ], $attributes)); } it('verweigert Nicht-Admins den Zugriff auf die Filter-Anfrage', function () { $this->actingAs(newsletterAdmin(0)); newsletterRequest(auth()->user(), ['source' => 'customers']); })->throws(AuthorizationException::class); it('lehnt ungültige Filterwerte ab', function () { $this->actingAs(newsletterAdmin()); newsletterRequest(auth()->user(), ['source' => 'alle', 'created_from' => '2026-02-01', 'created_to' => '2026-01-01']); })->throws(ValidationException::class); it('liefert die Vorschau als Datatable-JSON mit Kennzahlen', function () { $admin = newsletterAdmin(); $this->actingAs($admin); newsletterListCustomer(['billing_email' => 'a@example.com']); newsletterListCustomer(['billing_email' => 'A@example.com']); newsletterListCustomer(['billing_email' => 'b@example.com', 'subscribed' => 0]); $request = newsletterRequest($admin, ['source' => 'customers', 'draw' => 1, 'start' => 0, 'length' => 50]); $json = newsletterController()->datatable($request)->getData(true); expect($json['recordsTotal'])->toBe(2) ->and($json['stats']['total'])->toBe(2) ->and($json['stats']['duplicates'])->toBe(1) ->and(collect($json['data'])->pluck('email')->all())->toBe(['a@example.com', 'b@example.com']) ->and($json['data'][0]['name'])->toBe('Erika Muster') ->and($json['data'][0]['optin_label'])->toContain('badge-success') ->and($json['data'][1]['optin_label'])->toContain('badge-danger') ->and(get_user_attr('newsletter_list_filter')['source'])->toBe('customers'); }); it('exportiert die Liste als CSV mit BOM, Semikolon und Injection-Schutz', function () { $admin = newsletterAdmin(); $this->actingAs($admin); newsletterListCustomer(['billing_email' => 'csv@example.com', 'billing_firstname' => '=SUMME(A1)']); $response = newsletterController()->export(newsletterRequest($admin, ['source' => 'customers', 'template_only' => 1]), 'csv'); expect($response)->toBeInstanceOf(StreamedResponse::class); ob_start(); $response->sendContent(); $content = (string) ob_get_clean(); $lines = explode("\n", trim($content)); expect($content)->toStartWith("\xEF\xBB\xBF") ->and($lines[0])->toBe("\xEF\xBB\xBF".'Emailadresse;Vorname;Nachname;Anrede;Straße;PLZ;Wohnort;Firma;Berater') ->and($lines[1])->toBe("csv@example.com;'=SUMME(A1);Muster;Frau;\"Musterweg 1\";12345;Musterstadt;;") ->and($response->headers->get('content-disposition'))->toContain('newsletter_customers_de_'.now()->format('Y-m-d').'.csv'); }); it('exportiert die Liste als XLSX über die Export-Klasse', function () { Excel::fake(); $admin = newsletterAdmin(); $this->actingAs($admin); newsletterListCustomer(['billing_email' => 'xlsx@example.com']); newsletterController()->export(newsletterRequest($admin, ['source' => 'customers', 'language' => '']), 'xlsx'); Excel::assertDownloaded('newsletter_customers_alle_'.now()->format('Y-m-d').'.xlsx', function (xExport $export) { $rows = $export->collection(); return $export->headings()[0][0] === 'Emailadresse' && count($export->headings()[0]) === 17 && $rows->count() === 1 && $rows->first()[0] === 'xlsx@example.com'; }); }); it('zeigt die Index-Seite mit gemerktem Filter', function () { $admin = newsletterAdmin(); $this->actingAs($admin); set_user_attr('newsletter_list_filter', ['source' => 'both', 'language' => 'es', 'optin' => 'subscribed']); $view = newsletterController()->index(newsletterRequest($admin)); expect($view->getName())->toBe('admin.newsletter.index') ->and($view->getData()['filter']['source'])->toBe('both') ->and($view->getData()['filter']['language'])->toBe('es') ->and($view->getData()['filter']['optin'])->toBe('subscribed'); });