Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions e2e/tests/front-mypage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,68 @@ async function addDeliveryAddress(page: Page, addr01: string) {
await expect(page.locator('div.ec-pageHeader h1')).toContainText('お届け先一覧');
}

/**
* Helper: Return the order number of the most recently created order for the
* logged-in customer by reading the first "詳細を見る" link on the order history.
* The order detail link is /mypage/history/{order_no}.
*/
async function getLatestOrderNo(page: Page): Promise<string> {
await page.goto('/mypage/');
await page.waitForLoadState('load');
const href = await page.locator('p.ec-historyListHeader__action a').first().getAttribute('href');
const matched = href?.match(/\/mypage\/history\/(.+)$/);
expect(matched, `order detail link should contain an order_no: ${href}`).not.toBeNull();
return matched![1];
}

/**
* Helper: Set the tracking number (お問い合わせ番号) of the given order's first
* shipping via the admin order/shipping edit screen. Opens a separate admin page,
* logs in, searches for the order by its order number (so the test never depends
* on global ordering), and saves the tracking number.
*/
async function setTrackingNumberViaAdmin(page: Page, orderNo: string, trackingNumber: string) {
const adminRoute = process.env.ECCUBE_ADMIN_ROUTE || 'admin';

const adminPage = await page.context().newPage();
await adminPage.goto(`/${adminRoute}/`);
await adminPage.waitForLoadState('load');
await adminPage.locator('#login_id').fill(process.env.ADMIN_USER || 'admin');
await adminPage.locator('#password').fill(process.env.ADMIN_PASSWORD || 'password');
await adminPage.getByRole('button', { name: 'ログイン' }).click();
await adminPage.waitForLoadState('load');

// Search the order by its order number and open that specific order
await adminPage.goto(`/${adminRoute}/order`);
await adminPage.waitForLoadState('load');
await adminPage.locator('#admin_search_order_multi').fill(orderNo);
await adminPage.locator('#search_form #search_submit').click();
await adminPage.waitForLoadState('load');

const orderLink = adminPage.locator('table tbody td a[href*="/order/"]').first();
await expect(orderLink).toBeVisible();
const orderEditHref = await orderLink.getAttribute('href');
await adminPage.goto(orderEditHref!);
await adminPage.waitForLoadState('load');
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

// Navigate to the shipping edit page
const shippingEditLink = adminPage.locator('a[href*="/shipping/"][href*="/edit"]');
await expect(shippingEditLink).toBeVisible();
const shippingEditHref = await shippingEditLink.getAttribute('href');
await adminPage.goto(shippingEditHref!);
await adminPage.waitForLoadState('load');
await expect(adminPage.locator('.c-pageTitle')).toContainText('出荷登録');

// Fill the tracking number and save
await adminPage.locator('#form_shippings_0_tracking_number').fill(trackingNumber);
await adminPage.locator('button.ladda-button[type="submit"]').click();
await adminPage.waitForLoadState('load');
await expect(adminPage.locator('.alert-success')).toContainText('保存しました', { timeout: 30_000 });
await expect(adminPage.locator('#form_shippings_0_tracking_number')).toHaveValue(trackingNumber);

await adminPage.close();
}

test.describe('Front Mypage (EF05)', () => {

test('EF0501-UC01-T01 Mypage 初期表示', async ({ page }) => {
Expand Down Expand Up @@ -213,6 +275,48 @@ test.describe('Front Mypage (EF05)', () => {
await expect(totalBox).toContainText('合計');
});

test('EF0503-UC01-T03 Mypage ご注文履歴詳細 お問い合わせ番号表示', async ({ page }) => {
const trackingNumber = '1234567890123';

await loginAsTestCustomer(page);

// Create an order, capture its order number, then set its tracking number via admin
await createOrder(page);
const orderNo = await getLatestOrderNo(page);
await setTrackingNumberViaAdmin(page, orderNo, trackingNumber);

// Open that specific order's detail page directly (no dependency on list ordering)
await page.goto(`/mypage/history/${orderNo}`);
await page.waitForLoadState('load');
await expect(page.locator('div.ec-pageHeader h1')).toContainText('ご注文履歴詳細');

// The tracking number label and value are shown in the delivery section
const delivery = page.locator('div.ec-orderDelivery');
await expect(delivery).toContainText('お問い合わせ番号');
await expect(delivery).toContainText(trackingNumber);

// The tracking number is displayed right after the delivery time item
const labels = delivery.locator('div.ec-definitions--soft dt');
const deliveryTimeIndex = (await labels.allTextContents()).findIndex(t => t.includes('お届け時間'));
expect(deliveryTimeIndex).toBeGreaterThanOrEqual(0);
await expect(labels.nth(deliveryTimeIndex + 1)).toContainText('お問い合わせ番号');
});

test('EF0503-UC01-T04 Mypage ご注文履歴詳細 お問い合わせ番号未設定は非表示', async ({ page }) => {
await loginAsTestCustomer(page);

// Create an order without a tracking number, then open that specific order
await createOrder(page);
const orderNo = await getLatestOrderNo(page);
await page.goto(`/mypage/history/${orderNo}`);
await page.waitForLoadState('load');
await expect(page.locator('div.ec-pageHeader h1')).toContainText('ご注文履歴詳細');

// The tracking number item is not rendered
const delivery = page.locator('div.ec-orderDelivery');
await expect(delivery).not.toContainText('お問い合わせ番号');
});

test('EF0503-UC01-T02 Mypage お気に入り一覧', async ({ page }) => {
await loginAsTestCustomer(page);

Expand Down
1 change: 1 addition & 0 deletions src/Eccube/Resource/locale/messages.en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ front.mypage.delivery: Delivery to
front.mypage.delivery_provider: Delivery Method
front.mypage.delivery_date: Delivery Date
front.mypage.delivery_time: Delivery Time
front.mypage.tracking_number: Tracking No.
front.mypage.current_price: [Current Price]
front.mypage.reorder: Reorder
front.mypage.payment_info: Payment Info
Expand Down
1 change: 1 addition & 0 deletions src/Eccube/Resource/locale/messages.ja.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ front.mypage.delivery: お届け先
front.mypage.delivery_provider: 配送方法
front.mypage.delivery_date: お届け日
front.mypage.delivery_time: お届け時間
front.mypage.tracking_number: お問い合わせ番号
front.mypage.current_price: 【現在価格】
front.mypage.reorder: 再注文する
front.mypage.payment_info: お支払い情報
Expand Down
6 changes: 6 additions & 0 deletions src/Eccube/Resource/template/default/Mypage/history.twig
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ file that was distributed with this source code.
<dt>{{ 'front.mypage.delivery_time'|trans }} :</dt>
<dd>{{ Shipping.shipping_delivery_time|default('common.select__unspecified'|trans) }}</dd>
</div>
{% if Shipping.tracking_number %}
<div class="ec-definitions--soft">
<dt>{{ 'front.mypage.tracking_number'|trans }} :</dt>
<dd>{{ Shipping.tracking_number }}</dd>
</div>
{% endif %}
{% endfor %}
</div>
<div class="ec-orderPayment">
Expand Down
167 changes: 167 additions & 0 deletions tests/Eccube/Tests/Web/Mypage/MypageControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@
use Eccube\Entity\Customer;
use Eccube\Entity\CustomerFavoriteProduct;
use Eccube\Entity\Master\OrderStatus;
use Eccube\Entity\Order;
use Eccube\Entity\Product;
use Eccube\Entity\Shipping;
use Eccube\Tests\Fixture\Generator;
use Eccube\Tests\Web\AbstractWebTestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Contracts\Translation\TranslatorInterface;

final class MypageControllerTest extends AbstractWebTestCase
{
Expand Down Expand Up @@ -158,6 +161,170 @@ public function testHistoryWithNotFound()
$this->verify();
}

/**
* 注文履歴詳細を閲覧可能なステータス (NEW) の受注を生成する.
*
* createOrder() の既定ステータスは PROCESSING (仮受注) で mypage_history が 404 になるため,
* NEW へ遷移させてから返す.
*/
private function createOrderForHistory(): Order
{
$Order = $this->createOrder($this->Customer);
$Order->setOrderStatus($this->entityManager->find(OrderStatus::class, OrderStatus::NEW));

return $Order;
}

/**
* 受注に 2 つ目の配送先を追加し, お問い合わせ番号を設定する.
*/
private function addShipping(Order $Order, ?string $trackingNumber): Shipping
{
/** @var Shipping $Base */
$Base = $Order->getShippings()->first();

$Shipping = new Shipping();
$Shipping->copyProperties($this->Customer);
$Shipping
->setOrder($Order)
->setPref($Base->getPref())
->setDelivery($Base->getDelivery())
->setShippingDeliveryName($Base->getShippingDeliveryName())
->setTrackingNumber($trackingNumber)
->setCreateDate(new \DateTime())
->setUpdateDate(new \DateTime());
$Order->addShipping($Shipping);
$this->entityManager->persist($Shipping);

return $Shipping;
}

/**
* 注文履歴詳細をログイン会員として取得する.
*/
private function requestHistory(Order $Order): \Symfony\Component\DomCrawler\Crawler
{
$this->loginTo($this->Customer);

return $this->client->request(
Request::METHOD_GET,
$this->generateUrl('mypage_history', ['order_no' => $Order->getOrderNo()])
);
}

/**
* お問い合わせ番号が入力されている場合, 注文履歴詳細に表示される.
*/
public function testHistoryWithTrackingNumber()
{
$Order = $this->createOrderForHistory();
$Order->getShippings()->first()->setTrackingNumber('1234567890123');
$this->entityManager->flush();

$crawler = $this->requestHistory($Order);

$this->assertTrue($this->client->getResponse()->isSuccessful());
$this->assertCount(1, $crawler->filter('dt:contains("お問い合わせ番号")'));
$this->assertStringContainsString('1234567890123', $crawler->text());
}

/**
* お問い合わせ番号が null の場合, 項目自体が表示されない.
*/
public function testHistoryWithoutTrackingNumber()
{
$Order = $this->createOrderForHistory();
$Order->getShippings()->first()->setTrackingNumber(null);
$this->entityManager->flush();

$crawler = $this->requestHistory($Order);

$this->assertTrue($this->client->getResponse()->isSuccessful());
$this->assertCount(0, $crawler->filter('dt:contains("お問い合わせ番号")'));
}

/**
* お問い合わせ番号が空文字列の場合, 項目自体が表示されない.
*/
public function testHistoryWithEmptyTrackingNumber()
{
$Order = $this->createOrderForHistory();
$Order->getShippings()->first()->setTrackingNumber('');
$this->entityManager->flush();

$crawler = $this->requestHistory($Order);

$this->assertTrue($this->client->getResponse()->isSuccessful());
$this->assertCount(0, $crawler->filter('dt:contains("お問い合わせ番号")'));
}

/**
* 複数配送先がある場合, 入力済みの配送先ごとにお問い合わせ番号が表示される.
*/
public function testHistoryWithMultipleShippings()
{
$Order = $this->createOrderForHistory();
$Order->getShippings()->first()->setTrackingNumber('1111111111111');
$this->addShipping($Order, '2222222222222');
$this->entityManager->flush();

$crawler = $this->requestHistory($Order);

$this->assertTrue($this->client->getResponse()->isSuccessful());
$this->assertStringContainsString('1111111111111', $crawler->text());
$this->assertStringContainsString('2222222222222', $crawler->text());
// 配送先ごとに「お問い合わせ番号」ラベルが出力される (2 件).
$this->assertCount(2, $crawler->filter('dt:contains("お問い合わせ番号")'));
}

/**
* 複数配送先のうち一部のみ入力されている場合, 入力済みの配送先のみ表示される.
*/
public function testHistoryWithMultipleShippingsPartiallyFilled()
{
$Order = $this->createOrderForHistory();
$Order->getShippings()->first()->setTrackingNumber('1111111111111');
// 2 つ目の配送先はお問い合わせ番号未入力.
$this->addShipping($Order, null);
$this->entityManager->flush();

$crawler = $this->requestHistory($Order);

$this->assertTrue($this->client->getResponse()->isSuccessful());
$this->assertStringContainsString('1111111111111', $crawler->text());
// 入力済みは 1 件のみ.
$this->assertCount(1, $crawler->filter('dt:contains("お問い合わせ番号")'));
}

/**
* お問い合わせ番号に HTML 特殊文字が含まれていても自動エスケープされ, スクリプトが実行されない.
*/
public function testHistoryTrackingNumberXss()
{
$Order = $this->createOrderForHistory();
$Order->getShippings()->first()->setTrackingNumber('<script>alert("XSS")</script>');
$this->entityManager->flush();

$this->requestHistory($Order);

$this->assertTrue($this->client->getResponse()->isSuccessful());
$html = $this->client->getResponse()->getContent();
$this->assertStringContainsString('&lt;script&gt;', $html);
$this->assertStringNotContainsString('<script>alert', $html);
}

/**
* お問い合わせ番号ラベルの翻訳定義 (日本語 / 英語).
*/
public function testTrackingNumberLabelTranslations()
{
/** @var TranslatorInterface $translator */
$translator = static::getContainer()->get(TranslatorInterface::class);

$this->assertSame('お問い合わせ番号', $translator->trans('front.mypage.tracking_number', [], null, 'ja'));
$this->assertSame('Tracking No.', $translator->trans('front.mypage.tracking_number', [], null, 'en'));
}

Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
/**
* Paginator を経由したお気に入りの取得
*
Expand Down
Loading