mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-07-01 06:51:39 +00:00
Compare commits
2 commits
cee7e2a077
...
0140c9a7b9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0140c9a7b9 | ||
|
|
d25ac2622e |
3 changed files with 480 additions and 24 deletions
|
|
@ -1,8 +1,5 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
/**
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics)
|
||||
|
|
@ -20,23 +17,28 @@ declare(strict_types=1);
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\DataTables;
|
||||
|
||||
use App\DataTables\Adapters\TwoStepORMAdapter;
|
||||
use App\DataTables\Column\EntityColumn;
|
||||
use App\DataTables\Column\EnumColumn;
|
||||
use App\DataTables\Column\LocaleDateTimeColumn;
|
||||
use App\DataTables\Column\MarkdownColumn;
|
||||
use App\DataTables\Helpers\PartDataTableHelper;
|
||||
use App\Entity\Attachments\Attachment;
|
||||
use App\Doctrine\Helpers\FieldHelper;
|
||||
use App\Entity\Parts\Part;
|
||||
use App\Entity\Parts\ManufacturingStatus;
|
||||
use App\Entity\ProjectSystem\ProjectBOMEntry;
|
||||
use App\Services\ElementTypeNameGenerator;
|
||||
use App\Services\EntityURLGenerator;
|
||||
use App\Services\Formatters\AmountFormatter;
|
||||
use Doctrine\ORM\AbstractQuery;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Omines\DataTablesBundle\Adapter\Doctrine\ORM\SearchCriteriaProvider;
|
||||
use Omines\DataTablesBundle\Adapter\Doctrine\ORMAdapter;
|
||||
use Omines\DataTablesBundle\Column\TextColumn;
|
||||
use Omines\DataTablesBundle\DataTable;
|
||||
use Omines\DataTablesBundle\DataTableTypeInterface;
|
||||
|
|
@ -44,9 +46,12 @@ use Symfony\Contracts\Translation\TranslatorInterface;
|
|||
|
||||
class ProjectBomEntriesDataTable implements DataTableTypeInterface
|
||||
{
|
||||
public function __construct(protected TranslatorInterface $translator, protected PartDataTableHelper $partDataTableHelper,
|
||||
protected EntityURLGenerator $entityURLGenerator, protected AmountFormatter $amountFormatter)
|
||||
{
|
||||
public function __construct(
|
||||
protected EntityURLGenerator $entityURLGenerator,
|
||||
protected TranslatorInterface $translator,
|
||||
protected AmountFormatter $amountFormatter,
|
||||
protected PartDataTableHelper $partDataTableHelper
|
||||
) {
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -62,7 +67,7 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface
|
|||
return '';
|
||||
}
|
||||
return $this->partDataTableHelper->renderPicture($context->getPart());
|
||||
},
|
||||
}
|
||||
])
|
||||
|
||||
->add('id', TextColumn::class, [
|
||||
|
|
@ -133,23 +138,24 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface
|
|||
->add('category', EntityColumn::class, [
|
||||
'label' => $this->translator->trans('part.table.category'),
|
||||
'property' => 'part.category',
|
||||
'orderField' => 'NATSORT(category.name)',
|
||||
'orderField' => 'NATSORT(category.name)'
|
||||
])
|
||||
->add('footprint', EntityColumn::class, [
|
||||
'property' => 'part.footprint',
|
||||
'label' => $this->translator->trans('part.table.footprint'),
|
||||
'orderField' => 'NATSORT(footprint.name)',
|
||||
'orderField' => 'NATSORT(footprint.name)'
|
||||
])
|
||||
|
||||
->add('manufacturer', EntityColumn::class, [
|
||||
'property' => 'part.manufacturer',
|
||||
'label' => $this->translator->trans('part.table.manufacturer'),
|
||||
'orderField' => 'NATSORT(manufacturer.name)',
|
||||
'orderField' => 'NATSORT(manufacturer.name)'
|
||||
])
|
||||
|
||||
->add('manufacturing_status', EnumColumn::class, [
|
||||
'label' => $this->translator->trans('part.table.manufacturingStatus'),
|
||||
'data' => static fn(ProjectBOMEntry $context): ?ManufacturingStatus => $context->getPart()?->getManufacturingStatus(),
|
||||
'data' => static fn(ProjectBOMEntry $context): ?ManufacturingStatus => $context->getPart()?->getManufacturingStatus(),
|
||||
'orderField' => 'part.manufacturing_status',
|
||||
'class' => ManufacturingStatus::class,
|
||||
'render' => function (?ManufacturingStatus $status, ProjectBOMEntry $context): string {
|
||||
if ($status === null) {
|
||||
|
|
@ -183,8 +189,10 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface
|
|||
return '';
|
||||
}
|
||||
])
|
||||
->add('storageLocations', TextColumn::class, [
|
||||
'label' => 'part.table.storeLocations',
|
||||
->add('storelocation', TextColumn::class, [
|
||||
'label' => $this->translator->trans('part.table.storeLocations'),
|
||||
//We need to use a aggregate function to get the first store location, as we have a one-to-many relation
|
||||
'orderField' => 'NATSORT(MIN(_storelocations.name))',
|
||||
'visible' => false,
|
||||
'render' => function ($value, ProjectBOMEntry $context) {
|
||||
if ($context->getPart() !== null) {
|
||||
|
|
@ -207,11 +215,13 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface
|
|||
|
||||
$dataTable->addOrderBy('name', DataTable::SORT_ASCENDING);
|
||||
|
||||
$dataTable->createAdapter(ORMAdapter::class, [
|
||||
'entity' => Attachment::class,
|
||||
'query' => function (QueryBuilder $builder) use ($options): void {
|
||||
$this->getQuery($builder, $options);
|
||||
$dataTable->createAdapter(TwoStepORMAdapter::class, [
|
||||
'entity' => ProjectBOMEntry::class,
|
||||
'hydrate' => AbstractQuery::HYDRATE_OBJECT,
|
||||
'filter_query' => function (QueryBuilder $builder) use ($options): void {
|
||||
$this->getFilterQuery($builder, $options);
|
||||
},
|
||||
'detail_query' => $this->getDetailQuery(...),
|
||||
'criteria' => [
|
||||
function (QueryBuilder $builder) use ($options): void {
|
||||
$this->buildCriteria($builder, $options);
|
||||
|
|
@ -221,20 +231,71 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface
|
|||
]);
|
||||
}
|
||||
|
||||
private function getQuery(QueryBuilder $builder, array $options): void
|
||||
private function getFilterQuery(QueryBuilder $builder, array $options): void
|
||||
{
|
||||
$builder->select('bom_entry')
|
||||
->addSelect('part')
|
||||
$builder
|
||||
->select('bom_entry.id')
|
||||
->from(ProjectBOMEntry::class, 'bom_entry')
|
||||
->leftJoin('bom_entry.part', 'part')
|
||||
->leftJoin('part.category', 'category')
|
||||
->leftJoin('part.partLots', '_partLots')
|
||||
->leftJoin('_partLots.storage_location', '_storelocations')
|
||||
->leftJoin('part.footprint', 'footprint')
|
||||
->leftJoin('part.manufacturer', 'manufacturer')
|
||||
->leftJoin('part.partCustomState', 'partCustomState')
|
||||
->where('bom_entry.project = :project')
|
||||
->setParameter('project', $options['project'])
|
||||
->addGroupBy('bom_entry')
|
||||
->addGroupBy('part')
|
||||
->addGroupBy('category')
|
||||
->addGroupBy('footprint')
|
||||
->addGroupBy('manufacturer')
|
||||
->addGroupBy('partCustomState')
|
||||
;
|
||||
}
|
||||
|
||||
private function getDetailQuery(QueryBuilder $builder, array $filter_results): void
|
||||
{
|
||||
$ids = array_map(static fn (array $row) => $row['id'], $filter_results);
|
||||
if ($ids === []) {
|
||||
$ids = [-1];
|
||||
}
|
||||
|
||||
$builder
|
||||
->select('bom_entry')
|
||||
->addSelect('part')
|
||||
->addSelect('category')
|
||||
->addSelect('partLots')
|
||||
->addSelect('storelocations')
|
||||
->addSelect('footprint')
|
||||
->addSelect('manufacturer')
|
||||
->addSelect('partCustomState')
|
||||
->from(ProjectBOMEntry::class, 'bom_entry')
|
||||
->leftJoin('bom_entry.part', 'part')
|
||||
->leftJoin('part.category', 'category')
|
||||
->leftJoin('part.partLots', 'partLots')
|
||||
->leftJoin('partLots.storage_location', 'storelocations')
|
||||
->leftJoin('part.footprint', 'footprint')
|
||||
->leftJoin('part.manufacturer', 'manufacturer')
|
||||
->leftJoin('part.partCustomState', 'partCustomState')
|
||||
->where('bom_entry.id IN (:ids)')
|
||||
->setParameter('ids', $ids)
|
||||
->addGroupBy('bom_entry')
|
||||
->addGroupBy('part')
|
||||
->addGroupBy('partLots')
|
||||
->addGroupBy('category')
|
||||
->addGroupBy('storelocations')
|
||||
->addGroupBy('footprint')
|
||||
->addGroupBy('manufacturer')
|
||||
->addGroupBy('partCustomState')
|
||||
|
||||
->setHint(Query::HINT_READ_ONLY, true)
|
||||
->setHint(Query::HINT_FORCE_PARTIAL_LOAD, false)
|
||||
;
|
||||
|
||||
FieldHelper::addOrderByFieldParam($builder, 'bom_entry.id', 'ids');
|
||||
}
|
||||
|
||||
private function buildCriteria(QueryBuilder $builder, array $options): void
|
||||
{
|
||||
|
||||
|
|
|
|||
|
|
@ -280,9 +280,13 @@ class TMEProvider implements InfoProviderInterface, URLHandlerInfoProviderInterf
|
|||
{
|
||||
//If a URL starts with // we assume that it is a relative URL and we add the protocol
|
||||
if (str_starts_with($url, '//')) {
|
||||
return 'https:' . $url;
|
||||
$url = 'https:' . $url;
|
||||
}
|
||||
|
||||
//Encode bare % signs that are not already part of a valid percent-encoded sequence
|
||||
//Fixes part numbers with % in them e.g. SMD0603-5K1-1%
|
||||
$url = preg_replace('/%(?![0-9A-Fa-f]{2})/', '%25', $url);
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
|
|
|
|||
391
tests/Services/InfoProviderSystem/Providers/TMEProviderTest.php
Normal file
391
tests/Services/InfoProviderSystem/Providers/TMEProviderTest.php
Normal file
|
|
@ -0,0 +1,391 @@
|
|||
<?php
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Services\InfoProviderSystem\Providers;
|
||||
|
||||
use App\Entity\Parts\ManufacturingStatus;
|
||||
use App\Services\InfoProviderSystem\DTOs\PartDetailDTO;
|
||||
use App\Services\InfoProviderSystem\DTOs\PurchaseInfoDTO;
|
||||
use App\Services\InfoProviderSystem\DTOs\SearchResultDTO;
|
||||
use App\Services\InfoProviderSystem\Providers\ProviderCapabilities;
|
||||
use App\Services\InfoProviderSystem\Providers\TMEClient;
|
||||
use App\Services\InfoProviderSystem\Providers\TMEProvider;
|
||||
use App\Settings\InfoProviderSystem\TMESettings;
|
||||
use App\Tests\SettingsTestHelper;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpClient\MockHttpClient;
|
||||
use Symfony\Component\HttpClient\Response\MockResponse;
|
||||
|
||||
final class TMEProviderTest extends TestCase
|
||||
{
|
||||
private TMESettings $settings;
|
||||
private TMEProvider $provider;
|
||||
private MockHttpClient $httpClient;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->httpClient = new MockHttpClient();
|
||||
$this->settings = SettingsTestHelper::createSettingsDummy(TMESettings::class);
|
||||
// Use a short (anonymous-style) token so grossPrices is read from settings
|
||||
$this->settings->apiToken = 'test_token_000000000000000000000000000000000000000';
|
||||
$this->settings->apiSecret = 'test_secret';
|
||||
$this->settings->currency = 'EUR';
|
||||
$this->settings->language = 'en';
|
||||
$this->settings->country = 'DE';
|
||||
$this->settings->grossPrices = false;
|
||||
$this->provider = new TMEProvider(new TMEClient($this->httpClient, $this->settings), $this->settings);
|
||||
}
|
||||
|
||||
// --- Mock response helpers ---
|
||||
// Only fields actually read by TMEProvider are included.
|
||||
|
||||
private function mockProductList(array $products): MockResponse
|
||||
{
|
||||
return new MockResponse(json_encode([
|
||||
'Status' => 'OK',
|
||||
'Data' => ['ProductList' => $products],
|
||||
]));
|
||||
}
|
||||
|
||||
private function mockFilesList(array $products): MockResponse
|
||||
{
|
||||
return new MockResponse(json_encode([
|
||||
'Status' => 'OK',
|
||||
'Data' => ['ProductList' => $products],
|
||||
]));
|
||||
}
|
||||
|
||||
private function mockParametersList(array $products): MockResponse
|
||||
{
|
||||
return new MockResponse(json_encode([
|
||||
'Status' => 'OK',
|
||||
'Data' => ['ProductList' => $products],
|
||||
]));
|
||||
}
|
||||
|
||||
private function mockPrices(string $currency, string $priceType, array $products): MockResponse
|
||||
{
|
||||
return new MockResponse(json_encode([
|
||||
'Status' => 'OK',
|
||||
'Data' => [
|
||||
'Currency' => $currency,
|
||||
'PriceType' => $priceType,
|
||||
'ProductList' => $products,
|
||||
],
|
||||
]));
|
||||
}
|
||||
|
||||
// --- Mock data ---
|
||||
|
||||
private function smd0603Products(): MockResponse
|
||||
{
|
||||
return $this->mockProductList([[
|
||||
'Symbol' => 'SMD0603-5K1-1%',
|
||||
'OriginalSymbol' => '0603SAF5101T5E',
|
||||
'Producer' => 'ROYALOHM',
|
||||
'Description' => 'Resistor: thick film; SMD; 0603; 5.1kΩ; 0.1W; ±1%; 50V; -55÷155°C',
|
||||
'Category' => 'SMD resistors',
|
||||
'Photo' => '//ce8dc832c.cloudimg.io/v7/_cdn_/E9/C2/B0/00/0/732318_1.jpg',
|
||||
'ProductStatusList' => [],
|
||||
'ProductInformationPage' => '//www.tme.eu/en/details/smd0603-5k1-1%/smd-resistors/royalohm/0603saf5101t5e/',
|
||||
'Weight' => 0.021,
|
||||
'WeightUnit' => 'g',
|
||||
]]);
|
||||
}
|
||||
|
||||
private function smd0603Files(): MockResponse
|
||||
{
|
||||
return $this->mockFilesList([[
|
||||
'Symbol' => 'SMD0603-5K1-1%',
|
||||
'Files' => [
|
||||
'AdditionalPhotoList' => [],
|
||||
'DocumentList' => [
|
||||
['DocumentUrl' => '//www.tme.eu/Document/b315665a56acbc42df513c99b390ad98/ROYALOHM-THICKFILM.pdf'],
|
||||
['DocumentUrl' => '//www.tme.eu/Document/c283990e907c122bb808207d1578ac7f/POWER_RATING-DTE.pdf'],
|
||||
],
|
||||
],
|
||||
]]);
|
||||
}
|
||||
|
||||
private function smd0603Parameters(): MockResponse
|
||||
{
|
||||
return $this->mockParametersList([[
|
||||
'Symbol' => 'SMD0603-5K1-1%',
|
||||
'ParameterList' => [
|
||||
['ParameterId' => 34, 'ParameterName' => 'Type of resistor', 'ParameterValue' => 'thick film'],
|
||||
['ParameterId' => 35, 'ParameterName' => 'Case - mm', 'ParameterValue' => '1608'],
|
||||
['ParameterId' => 38, 'ParameterName' => 'Resistance', 'ParameterValue' => '5.1kΩ'],
|
||||
['ParameterId' => 39, 'ParameterName' => 'Tolerance', 'ParameterValue' => '±1%'],
|
||||
['ParameterId' => 120, 'ParameterName' => 'Operating voltage', 'ParameterValue' => '50V'],
|
||||
],
|
||||
]]);
|
||||
}
|
||||
|
||||
private function smd0603Prices(): MockResponse
|
||||
{
|
||||
return $this->mockPrices('EUR', 'NET', [[
|
||||
'Symbol' => 'SMD0603-5K1-1%',
|
||||
'PriceList' => [
|
||||
['Amount' => 100, 'PriceValue' => 0.01077],
|
||||
['Amount' => 1000, 'PriceValue' => 0.00291],
|
||||
['Amount' => 5000, 'PriceValue' => 0.00150],
|
||||
],
|
||||
]]);
|
||||
}
|
||||
|
||||
private function etqp3mProducts(): MockResponse
|
||||
{
|
||||
return $this->mockProductList([[
|
||||
'Symbol' => 'ETQP3M6R8KVP',
|
||||
'OriginalSymbol' => 'ETQP3M6R8KVP',
|
||||
'Producer' => 'PANASONIC',
|
||||
'Description' => 'Inductor: wire; SMD; 6.8uH; 2.9A; R: 65.7mΩ; ±20%; ETQP3M; 5.5x5x3mm',
|
||||
'Category' => 'Inductors',
|
||||
'Photo' => '//ce8dc832c.cloudimg.io/v7/_cdn_/9E/27/A0/00/0/684777_1.jpg',
|
||||
'ProductStatusList' => [],
|
||||
'ProductInformationPage' => '//www.tme.eu/en/details/etqp3m6r8kvp/inductors/panasonic/',
|
||||
'Weight' => 0.44,
|
||||
'WeightUnit' => 'g',
|
||||
]]);
|
||||
}
|
||||
|
||||
private function etqp3mFiles(): MockResponse
|
||||
{
|
||||
return $this->mockFilesList([[
|
||||
'Symbol' => 'ETQP3M6R8KVP',
|
||||
'Files' => [
|
||||
'AdditionalPhotoList' => [],
|
||||
'DocumentList' => [
|
||||
['DocumentUrl' => '//www.tme.eu/Document/50a845881f09d8a2248350946e11df38/AGL0000C63.pdf'],
|
||||
['DocumentUrl' => '//www.tme.eu/Document/8480690a42fa577214e35e33d3fc8d77/ETQP3M100KVN-LNK.txt'],
|
||||
],
|
||||
],
|
||||
]]);
|
||||
}
|
||||
|
||||
private function etqp3mParameters(): MockResponse
|
||||
{
|
||||
return $this->mockParametersList([[
|
||||
'Symbol' => 'ETQP3M6R8KVP',
|
||||
'ParameterList' => [
|
||||
['ParameterId' => 566, 'ParameterName' => 'Inductance', 'ParameterValue' => '6.8µH'],
|
||||
['ParameterId' => 370, 'ParameterName' => 'Operating current', 'ParameterValue' => '2.9A'],
|
||||
['ParameterId' => 39, 'ParameterName' => 'Tolerance', 'ParameterValue' => '±20%'],
|
||||
],
|
||||
]]);
|
||||
}
|
||||
|
||||
private function etqp3mPrices(): MockResponse
|
||||
{
|
||||
return $this->mockPrices('EUR', 'NET', [[
|
||||
'Symbol' => 'ETQP3M6R8KVP',
|
||||
'PriceList' => [
|
||||
['Amount' => 1, 'PriceValue' => 0.589],
|
||||
['Amount' => 5, 'PriceValue' => 0.429],
|
||||
['Amount' => 10, 'PriceValue' => 0.399],
|
||||
],
|
||||
]]);
|
||||
}
|
||||
|
||||
// --- Tests ---
|
||||
|
||||
public function testGetProviderInfo(): void
|
||||
{
|
||||
$info = $this->provider->getProviderInfo();
|
||||
|
||||
$this->assertIsArray($info);
|
||||
$this->assertArrayHasKey('name', $info);
|
||||
$this->assertArrayHasKey('description', $info);
|
||||
$this->assertArrayHasKey('url', $info);
|
||||
$this->assertEquals('TME', $info['name']);
|
||||
$this->assertEquals('https://tme.eu/', $info['url']);
|
||||
}
|
||||
|
||||
public function testGetProviderKey(): void
|
||||
{
|
||||
$this->assertSame('tme', $this->provider->getProviderKey());
|
||||
}
|
||||
|
||||
public function testIsActiveWithCredentials(): void
|
||||
{
|
||||
$this->assertTrue($this->provider->isActive());
|
||||
}
|
||||
|
||||
public function testIsActiveWithoutCredentials(): void
|
||||
{
|
||||
$this->settings->apiToken = null;
|
||||
$provider = new TMEProvider(new TMEClient($this->httpClient, $this->settings), $this->settings);
|
||||
$this->assertFalse($provider->isActive());
|
||||
}
|
||||
|
||||
public function testGetCapabilities(): void
|
||||
{
|
||||
$capabilities = $this->provider->getCapabilities();
|
||||
|
||||
$this->assertIsArray($capabilities);
|
||||
$this->assertContains(ProviderCapabilities::BASIC, $capabilities);
|
||||
$this->assertContains(ProviderCapabilities::PICTURE, $capabilities);
|
||||
$this->assertContains(ProviderCapabilities::DATASHEET, $capabilities);
|
||||
$this->assertContains(ProviderCapabilities::PRICE, $capabilities);
|
||||
$this->assertContains(ProviderCapabilities::FOOTPRINT, $capabilities);
|
||||
}
|
||||
|
||||
public function testGetHandledDomains(): void
|
||||
{
|
||||
$this->assertContains('tme.eu', $this->provider->getHandledDomains());
|
||||
}
|
||||
|
||||
public function testGetIDFromURL(): void
|
||||
{
|
||||
$this->assertSame('fi321_se', $this->provider->getIDFromURL('https://www.tme.eu/de/details/fi321_se/kuhler/alutronic/'));
|
||||
$this->assertSame('smd0603-5k1-1%25', $this->provider->getIDFromURL('https://www.tme.eu/en/details/smd0603-5k1-1%25/smd-resistors/royalohm/0603saf5101t5e/'));
|
||||
$this->assertNull($this->provider->getIDFromURL('https://www.tme.eu/en/'));
|
||||
}
|
||||
|
||||
public function testSearchByKeyword(): void
|
||||
{
|
||||
$this->httpClient->setResponseFactory([$this->smd0603Products()]);
|
||||
|
||||
$results = $this->provider->searchByKeyword('SMD0603-5K1-1%');
|
||||
|
||||
$this->assertIsArray($results);
|
||||
$this->assertCount(1, $results);
|
||||
$this->assertInstanceOf(SearchResultDTO::class, $results[0]);
|
||||
$this->assertSame('SMD0603-5K1-1%', $results[0]->provider_id);
|
||||
$this->assertSame('0603SAF5101T5E', $results[0]->name);
|
||||
$this->assertSame('ROYALOHM', $results[0]->manufacturer);
|
||||
$this->assertSame('SMD resistors', $results[0]->category);
|
||||
$this->assertSame(ManufacturingStatus::ACTIVE, $results[0]->manufacturing_status);
|
||||
$this->assertSame(
|
||||
'https://www.tme.eu/en/details/smd0603-5k1-1%25/smd-resistors/royalohm/0603saf5101t5e/',
|
||||
$results[0]->provider_url
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetDetailsWithPercentInPartNumber(): void
|
||||
{
|
||||
$this->httpClient->setResponseFactory([
|
||||
$this->smd0603Products(),
|
||||
$this->smd0603Files(),
|
||||
$this->smd0603Parameters(),
|
||||
$this->smd0603Prices(),
|
||||
]);
|
||||
|
||||
$result = $this->provider->getDetails('SMD0603-5K1-1%');
|
||||
|
||||
$this->assertInstanceOf(PartDetailDTO::class, $result);
|
||||
$this->assertSame('SMD0603-5K1-1%', $result->provider_id);
|
||||
$this->assertSame('0603SAF5101T5E', $result->name);
|
||||
$this->assertSame('Resistor: thick film; SMD; 0603; 5.1kΩ; 0.1W; ±1%; 50V; -55÷155°C', $result->description);
|
||||
$this->assertSame('ROYALOHM', $result->manufacturer);
|
||||
$this->assertSame('0603SAF5101T5E', $result->mpn);
|
||||
$this->assertSame('SMD resistors', $result->category);
|
||||
$this->assertSame(ManufacturingStatus::ACTIVE, $result->manufacturing_status);
|
||||
$this->assertSame(0.021, $result->mass);
|
||||
$this->assertSame('1608', $result->footprint);
|
||||
$this->assertSame(
|
||||
'https://www.tme.eu/en/details/smd0603-5k1-1%25/smd-resistors/royalohm/0603saf5101t5e/',
|
||||
$result->provider_url
|
||||
);
|
||||
|
||||
$this->assertCount(2, $result->datasheets);
|
||||
$this->assertSame('https://www.tme.eu/Document/b315665a56acbc42df513c99b390ad98/ROYALOHM-THICKFILM.pdf', $result->datasheets[0]->url);
|
||||
$this->assertCount(0, $result->images);
|
||||
|
||||
$this->assertCount(1, $result->vendor_infos);
|
||||
$vendorInfo = $result->vendor_infos[0];
|
||||
$this->assertInstanceOf(PurchaseInfoDTO::class, $vendorInfo);
|
||||
$this->assertSame('TME', $vendorInfo->distributor_name);
|
||||
$this->assertSame('SMD0603-5K1-1%', $vendorInfo->order_number);
|
||||
$this->assertSame(
|
||||
'https://www.tme.eu/en/details/smd0603-5k1-1%25/smd-resistors/royalohm/0603saf5101t5e/',
|
||||
$vendorInfo->product_url
|
||||
);
|
||||
$this->assertCount(3, $vendorInfo->prices);
|
||||
$this->assertSame(100.0, $vendorInfo->prices[0]->minimum_discount_amount);
|
||||
$this->assertSame('0.01077', $vendorInfo->prices[0]->price);
|
||||
$this->assertSame('EUR', $vendorInfo->prices[0]->currency_iso_code);
|
||||
$this->assertFalse($vendorInfo->prices[0]->includes_tax);
|
||||
|
||||
$this->assertCount(5, $result->parameters);
|
||||
}
|
||||
|
||||
public function testGetDetailsForEtqp3m6r8kvp(): void
|
||||
{
|
||||
$this->httpClient->setResponseFactory([
|
||||
$this->etqp3mProducts(),
|
||||
$this->etqp3mFiles(),
|
||||
$this->etqp3mParameters(),
|
||||
$this->etqp3mPrices(),
|
||||
]);
|
||||
|
||||
$result = $this->provider->getDetails('ETQP3M6R8KVP');
|
||||
|
||||
$this->assertInstanceOf(PartDetailDTO::class, $result);
|
||||
$this->assertSame('ETQP3M6R8KVP', $result->provider_id);
|
||||
$this->assertSame('ETQP3M6R8KVP', $result->name);
|
||||
$this->assertSame('Inductor: wire; SMD; 6.8uH; 2.9A; R: 65.7mΩ; ±20%; ETQP3M; 5.5x5x3mm', $result->description);
|
||||
$this->assertSame('PANASONIC', $result->manufacturer);
|
||||
$this->assertSame('ETQP3M6R8KVP', $result->mpn);
|
||||
$this->assertSame('Inductors', $result->category);
|
||||
$this->assertSame(ManufacturingStatus::ACTIVE, $result->manufacturing_status);
|
||||
$this->assertSame(0.44, $result->mass);
|
||||
$this->assertNull($result->footprint);
|
||||
$this->assertSame('https://www.tme.eu/en/details/etqp3m6r8kvp/inductors/panasonic/', $result->provider_url);
|
||||
|
||||
$this->assertCount(2, $result->datasheets);
|
||||
$this->assertSame('https://www.tme.eu/Document/50a845881f09d8a2248350946e11df38/AGL0000C63.pdf', $result->datasheets[0]->url);
|
||||
$this->assertCount(0, $result->images);
|
||||
|
||||
$this->assertCount(1, $result->vendor_infos);
|
||||
$vendorInfo = $result->vendor_infos[0];
|
||||
$this->assertSame('TME', $vendorInfo->distributor_name);
|
||||
$this->assertSame('ETQP3M6R8KVP', $vendorInfo->order_number);
|
||||
$this->assertSame('https://www.tme.eu/en/details/etqp3m6r8kvp/inductors/panasonic/', $vendorInfo->product_url);
|
||||
$this->assertCount(3, $vendorInfo->prices);
|
||||
$this->assertSame(1.0, $vendorInfo->prices[0]->minimum_discount_amount);
|
||||
$this->assertSame('0.589', $vendorInfo->prices[0]->price);
|
||||
$this->assertSame('EUR', $vendorInfo->prices[0]->currency_iso_code);
|
||||
$this->assertFalse($vendorInfo->prices[0]->includes_tax);
|
||||
|
||||
$this->assertCount(3, $result->parameters);
|
||||
}
|
||||
|
||||
public function testNormalizeURLEncodesBarePctSign(): void
|
||||
{
|
||||
$method = (new \ReflectionClass($this->provider))->getMethod('normalizeURL');
|
||||
|
||||
$this->assertSame(
|
||||
'https://www.tme.eu/en/details/smd0603-5k1-1%25/smd-resistors/royalohm/0603saf5101t5e/',
|
||||
$method->invoke($this->provider, '//www.tme.eu/en/details/smd0603-5k1-1%/smd-resistors/royalohm/0603saf5101t5e/')
|
||||
);
|
||||
$this->assertSame(
|
||||
'https://www.tme.eu/en/details/smd0603-5k1-1%25/smd-resistors/royalohm/0603saf5101t5e/',
|
||||
$method->invoke($this->provider, '//www.tme.eu/en/details/smd0603-5k1-1%25/smd-resistors/royalohm/0603saf5101t5e/')
|
||||
);
|
||||
$this->assertSame(
|
||||
'https://www.tme.eu/en/details/etqp3m6r8kvp/inductors/panasonic/',
|
||||
$method->invoke($this->provider, '//www.tme.eu/en/details/etqp3m6r8kvp/inductors/panasonic/')
|
||||
);
|
||||
$this->assertSame('https://example.com/path', $method->invoke($this->provider, 'https://example.com/path'));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue