setAccessible(true); return $reflection->invoke($service, ...$args); } /** * Formázott (rich text) cella: a terméknév egy része félkövér. A PhpSpreadsheet ilyenkor * nem stringet, hanem RichText objektumot ad vissza a getValue()-ból. */ function richTextCellValue(): RichText { $richText = new RichText(); $richText->createText('Trappista sajt '); $richText->createTextRun('PRÉMIUM')->getFont()->setBold(true); return $richText; } test('a rich text cella értéke sima szövegként normalizálódik', function () { // json_encode a nyers objektumot üresre serializálná (a szövegrészek protected // propertyben vannak), így a payloadba üres tömb kerülne és a szöveg elveszne. expect(json_encode(['x' => richTextCellValue()]))->toBe('{"x":{}}'); expect(invokeProtected('normalizeCellValue', richTextCellValue())) ->toBe('Trappista sajt PRÉMIUM'); }); test('a normalizálás a skalárokat változatlanul hagyja', function () { expect(invokeProtected('normalizeCellValue', 'Sajt'))->toBe('Sajt') ->and(invokeProtected('normalizeCellValue', 1250.5))->toBe(1250.5) ->and(invokeProtected('normalizeCellValue', null))->toBeNull(); }); test('a dátum cella szöveggé alakul, nem tömbbé', function () { expect(invokeProtected('normalizeCellValue', new DateTimeImmutable('2026-08-24 10:30:00'))) ->toBe('2026-08-24 10:30:00'); }); test('az üres tömb hiányzó kötelező értéknek számít', function () { // A régi rekordokban a rich text cellák üres tömbként landoltak. A korábbi, // is_string() alapú vizsgálat ezeket KITÖLTÖTTNEK látta, így egy elveszett // terméknév némán átment a kötelező mező ellenőrzésén. expect(invokeProtected('isBlankPayloadValue', []))->toBeTrue() ->and(invokeProtected('isBlankPayloadValue', null))->toBeTrue() ->and(invokeProtected('isBlankPayloadValue', ' '))->toBeTrue() ->and(invokeProtected('isBlankPayloadValue', 'Sajt'))->toBeFalse() ->and(invokeProtected('isBlankPayloadValue', 0))->toBeFalse(); }); test('a már elmentett tömbös payload nem töri el a sorok táblát', function () { $supplier = Supplier::factory()->create(); $file = PricelistFile::create([ 'filename' => 'arlista.xlsx', 'supplier_id' => $supplier->id, 'available_date' => now()->addWeek()->toDateString(), 'status' => PricelistFileStatusEnum::waiting_for_approval, ]); // Pontosan az az állapot, ami a d2d-n a nézetet eltörte: // TextColumn::getDescriptionBelow(): Return value must be of type // Htmlable|string|null, array returned PricelistFileLine::create([ 'pricelist_file_id' => $file->id, 'row_number' => 5, 'status' => PricelistFileLineStatusEnum::error, 'payload' => [ PriceListService::EXPECTED_HEADERS[0] => 'SKU-1', PriceListService::EXPECTED_HEADERS[4] => [], ], 'validation_messages' => ['name' => 'Hiányzó kötelező mező'], ]); $user = User::factory()->create(); $user->addRole(Role::create(['name' => 'admin', 'display_name' => 'Admin'])); $this->actingAs($user); Livewire::test(LinesRelationManager::class, [ 'ownerRecord' => $file, 'pageClass' => ViewPricelistFile::class, ]) ->assertOk() ->assertSee('SKU-1'); });