feat(translation,core-flows): translate remaining core entities and sync shipping option <> method translations (#14358)

* Get translated shipping options step

* Apply translations on shipping options list methods.

* Pass shipping option naem when refreshing cart shipping methods, so if locale changed, we update the name

* Update translatable fields config

* Cart shipping method update translation tests

* Shipping options translations tests

* Add changeset

* Update order shipping method translations on update

* Remove unnecessary workflow and use step instead

* Translate shipping method on order edit

* Use new update shipping methods tranlsations step

* Draft order shipping method translation sync

* Translate shipping method on order exchange

* Translate returns shipping methods

* Translate claims shipping methods

* Remove unnecessary check

* Early return

* Fix import

---------

Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
Nicolas Gorga
2026-01-02 07:26:06 -03:00
committed by GitHub
parent 5f807ee657
commit 11de7e3e34
25 changed files with 2831 additions and 27 deletions

View File

@@ -515,6 +515,229 @@ medusaIntegrationTestRunner({
expect(allItemsTranslated).toBe(true)
})
})
describe("POST /store/carts/:id/shipping-methods (shipping method translation)", () => {
let shippingOption
beforeEach(async () => {
const stockLocation = (
await api.post(
`/admin/stock-locations`,
{ name: "translation test location" },
adminHeaders
)
).data.stock_location
await api.post(
`/admin/stock-locations/${stockLocation.id}/sales-channels`,
{ add: [salesChannel.id] },
adminHeaders
)
const fulfillmentSets = (
await api.post(
`/admin/stock-locations/${stockLocation.id}/fulfillment-sets?fields=*fulfillment_sets`,
{
name: `Translation-Test-${shippingProfile.id}`,
type: "test-type",
},
adminHeaders
)
).data.stock_location.fulfillment_sets
const fulfillmentSet = (
await api.post(
`/admin/fulfillment-sets/${fulfillmentSets[0].id}/service-zones`,
{
name: `Translation-Test-${shippingProfile.id}`,
geo_zones: [{ type: "country", country_code: "US" }],
},
adminHeaders
)
).data.fulfillment_set
await api.post(
`/admin/stock-locations/${stockLocation.id}/fulfillment-providers`,
{ add: ["manual_test-provider"] },
adminHeaders
)
shippingOption = (
await api.post(
`/admin/shipping-options`,
{
name: "Standard Shipping",
service_zone_id: fulfillmentSet.service_zones[0].id,
shipping_profile_id: shippingProfile.id,
provider_id: "manual_test-provider",
price_type: "flat",
type: {
label: "Standard",
description: "Standard shipping option",
code: "standard",
},
prices: [{ currency_code: "usd", amount: 1000 }],
rules: [],
},
adminHeaders
)
).data.shipping_option
// Create translations for shipping option
await api.post(
"/admin/translations/batch",
{
create: [
{
reference_id: shippingOption.id,
reference: "shipping_option",
locale_code: "fr-FR",
translations: {
name: "Expédition Standard",
},
},
{
reference_id: shippingOption.id,
reference: "shipping_option",
locale_code: "de-DE",
translations: {
name: "Standardversand",
},
},
],
},
adminHeaders
)
})
it("should add shipping method with translated name when cart has locale", async () => {
const cartResponse = await api.post(
`/store/carts`,
{
currency_code: "usd",
sales_channel_id: salesChannel.id,
region_id: region.id,
locale: "fr-FR",
shipping_address: shippingAddressData,
items: [{ variant_id: product.variants[0].id, quantity: 1 }],
},
storeHeaders
)
const cart = cartResponse.data.cart
await api.post(
`/store/carts/${cart.id}/shipping-methods`,
{ option_id: shippingOption.id },
storeHeaders
)
const updatedCart = await api
.get(
`/store/carts/${cart.id}?fields=+shipping_methods.name`,
storeHeaders
)
.then((res) => res.data.cart)
expect(updatedCart.shipping_methods).toHaveLength(1)
expect(updatedCart.shipping_methods[0]).toEqual(
expect.objectContaining({
shipping_option_id: shippingOption.id,
name: "Expédition Standard",
})
)
})
it("should update shipping method name when cart locale changes", async () => {
const cartResponse = await api.post(
`/store/carts`,
{
currency_code: "usd",
sales_channel_id: salesChannel.id,
region_id: region.id,
locale: "fr-FR",
shipping_address: shippingAddressData,
items: [{ variant_id: product.variants[0].id, quantity: 1 }],
},
storeHeaders
)
const cart = cartResponse.data.cart
// Add shipping method with French locale
await api.post(
`/store/carts/${cart.id}/shipping-methods`,
{ option_id: shippingOption.id },
storeHeaders
)
// Verify French translation
let cartResponseAfter = await api.get(
`/store/carts/${cart.id}?fields=+shipping_methods.name`,
storeHeaders
)
expect(cartResponseAfter.data.cart.shipping_methods[0].name).toEqual(
"Expédition Standard"
)
// Update cart locale to German
await api.post(
`/store/carts/${cart.id}`,
{
locale: "de-DE",
},
storeHeaders
)
// Verify German translation
cartResponseAfter = await api.get(
`/store/carts/${cart.id}?fields=+shipping_methods.name`,
storeHeaders
)
expect(cartResponseAfter.data.cart.shipping_methods[0].name).toEqual(
"Standardversand"
)
})
it("should use original shipping option name when no translation exists", async () => {
const cartResponse = await api.post(
`/store/carts`,
{
currency_code: "usd",
sales_channel_id: salesChannel.id,
region_id: region.id,
locale: "ja-JP",
shipping_address: shippingAddressData,
items: [{ variant_id: product.variants[0].id, quantity: 1 }],
},
storeHeaders
)
const cart = cartResponse.data.cart
await api.post(
`/store/carts/${cart.id}/shipping-methods`,
{ option_id: shippingOption.id },
storeHeaders
)
const updatedCart = await api
.get(
`/store/carts/${cart.id}?fields=+shipping_methods.name`,
storeHeaders
)
.then((res) => res.data.cart)
expect(updatedCart.shipping_methods[0]).toEqual(
expect.objectContaining({
shipping_option_id: shippingOption.id,
name: "Standard Shipping",
})
)
})
})
})
},
})

View File

@@ -0,0 +1,739 @@
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
import {
ClaimReason,
ClaimType,
Modules,
ProductStatus,
RuleOperator,
} from "@medusajs/utils"
import {
adminHeaders,
createAdminUser,
} from "../../../helpers/create-admin-user"
jest.setTimeout(60000)
process.env.MEDUSA_FF_TRANSLATION = "true"
medusaIntegrationTestRunner({
testSuite: ({ dbConnection, getContainer, api }) => {
let order
let customer
let returnShippingOption
let outboundShippingOption
let shippingProfile
let fulfillmentSet
let inventoryItem
let location
let salesChannel
let region
let product
let productExtra
beforeEach(async () => {
const container = getContainer()
await createAdminUser(dbConnection, adminHeaders, container)
customer = (
await api.post(
"/admin/customers",
{
first_name: "joe",
email: "joe@admin.com",
},
adminHeaders
)
).data.customer
// Set up supported locales in the store
const storeModule = container.resolve(Modules.STORE)
const [defaultStore] = await storeModule.listStores(
{},
{ select: ["id"], take: 1 }
)
await storeModule.updateStores(defaultStore.id, {
supported_locales: [
{ locale_code: "en-US" },
{ locale_code: "fr-FR" },
{ locale_code: "de-DE" },
],
})
salesChannel = (
await api.post(
"/admin/sales-channels",
{ name: "Webshop", description: "channel" },
adminHeaders
)
).data.sales_channel
shippingProfile = (
await api.post(
`/admin/shipping-profiles`,
{ name: "Test", type: "default" },
adminHeaders
)
).data.shipping_profile
region = (
await api.post(
"/admin/regions",
{
name: "Test Region",
currency_code: "usd",
},
adminHeaders
)
).data.region
location = (
await api.post(
`/admin/stock-locations`,
{ name: "Test location" },
adminHeaders
)
).data.stock_location
inventoryItem = (
await api.post(
`/admin/inventory-items`,
{ sku: "test-variant" },
adminHeaders
)
).data.inventory_item
await api.post(
`/admin/inventory-items/${inventoryItem.id}/location-levels`,
{ location_id: location.id, stocked_quantity: 10 },
adminHeaders
)
await api.post(
`/admin/stock-locations/${location.id}/sales-channels`,
{ add: [salesChannel.id] },
adminHeaders
)
product = (
await api.post(
"/admin/products",
{
title: "Test product",
status: ProductStatus.PUBLISHED,
options: [{ title: "size", values: ["large", "small"] }],
shipping_profile_id: shippingProfile.id,
variants: [
{
title: "Test variant",
sku: "test-variant",
options: { size: "large" },
inventory_items: [
{
inventory_item_id: inventoryItem.id,
required_quantity: 1,
},
],
prices: [{ currency_code: "usd", amount: 25 }],
},
],
},
adminHeaders
)
).data.product
productExtra = (
await api.post(
"/admin/products",
{
title: "Extra product",
status: ProductStatus.PUBLISHED,
options: [{ title: "size", values: ["large", "small"] }],
shipping_profile_id: shippingProfile.id,
variants: [
{
title: "Extra variant",
sku: "extra-variant",
options: { size: "large" },
manage_inventory: false,
prices: [{ currency_code: "usd", amount: 50 }],
},
],
},
adminHeaders
)
).data.product
location = (
await api.post(
`/admin/stock-locations/${location.id}/fulfillment-sets?fields=*fulfillment_sets`,
{ name: "Test", type: "test-type" },
adminHeaders
)
).data.stock_location
fulfillmentSet = (
await api.post(
`/admin/fulfillment-sets/${location.fulfillment_sets[0].id}/service-zones`,
{
name: "Test",
geo_zones: [{ type: "country", country_code: "us" }],
},
adminHeaders
)
).data.fulfillment_set
await api.post(
`/admin/stock-locations/${location.id}/fulfillment-providers`,
{ add: ["manual_test-provider"] },
adminHeaders
)
returnShippingOption = (
await api.post(
"/admin/shipping-options",
{
name: "Return shipping",
service_zone_id: fulfillmentSet.service_zones[0].id,
shipping_profile_id: shippingProfile.id,
provider_id: "manual_test-provider",
price_type: "flat",
type: {
label: "Test type",
description: "Test description",
code: "test-code",
},
prices: [{ currency_code: "usd", amount: 1500 }],
rules: [
{
operator: RuleOperator.EQ,
attribute: "is_return",
value: "true",
},
],
},
adminHeaders
)
).data.shipping_option
outboundShippingOption = (
await api.post(
"/admin/shipping-options",
{
name: "Outbound shipping",
service_zone_id: fulfillmentSet.service_zones[0].id,
shipping_profile_id: shippingProfile.id,
provider_id: "manual_test-provider",
price_type: "flat",
type: {
label: "Test type",
description: "Test description",
code: "test-code",
},
prices: [{ currency_code: "usd", amount: 0 }],
rules: [
{
operator: RuleOperator.EQ,
attribute: "is_return",
value: "false",
},
],
},
adminHeaders
)
).data.shipping_option
// Create translations for shipping options
await api.post(
"/admin/translations/batch",
{
create: [
{
reference_id: returnShippingOption.id,
reference: "shipping_option",
locale_code: "fr-FR",
translations: { name: "Expédition de retour" },
},
{
reference_id: returnShippingOption.id,
reference: "shipping_option",
locale_code: "de-DE",
translations: { name: "Rückversand" },
},
{
reference_id: outboundShippingOption.id,
reference: "shipping_option",
locale_code: "fr-FR",
translations: { name: "Expédition sortante" },
},
{
reference_id: outboundShippingOption.id,
reference: "shipping_option",
locale_code: "de-DE",
translations: { name: "Ausgehende Versand" },
},
],
},
adminHeaders
)
})
const createOrderWithLocale = async (locale?: string) => {
const container = getContainer()
const orderModule = container.resolve(Modules.ORDER)
const inventoryModule = container.resolve(Modules.INVENTORY)
const createdOrder = await orderModule.createOrders({
region_id: region.id,
email: "foo@bar.com",
locale,
items: [
{
title: "Custom Item",
variant_id: product.variants[0].id,
quantity: 2,
unit_price: 25,
},
],
sales_channel_id: salesChannel.id,
shipping_address: {
customer_id: customer.id,
first_name: "Test",
last_name: "Test",
address_1: "Test",
city: "Test",
country_code: "US",
postal_code: "12345",
phone: "12345",
},
billing_address: {
customer_id: customer.id,
first_name: "Test",
last_name: "Test",
address_1: "Test",
city: "Test",
country_code: "US",
postal_code: "12345",
},
shipping_methods: [
{
name: "Test shipping method",
amount: 10,
data: {},
},
],
currency_code: "usd",
customer_id: customer.id,
transactions: [
{
amount: 60,
currency_code: "usd",
},
],
})
await inventoryModule.createReservationItems([
{
inventory_item_id: inventoryItem.id,
location_id: location.id,
quantity: 2,
line_item_id: createdOrder.items![0].id,
},
])
// Fulfill the order
await api.post(
`/admin/orders/${createdOrder.id}/fulfillments`,
{
items: [
{
id: createdOrder.items![0].id,
quantity: 2,
},
],
},
adminHeaders
)
return createdOrder
}
describe("Claim shipping method translation", () => {
describe("Inbound (return) shipping method", () => {
it("should translate inbound shipping method name using French locale", async () => {
order = await createOrderWithLocale("fr-FR")
const claim = (
await api.post(
"/admin/claims",
{
order_id: order.id,
type: ClaimType.REPLACE,
description: "Test claim",
},
adminHeaders
)
).data.claim
await api.post(
`/admin/claims/${claim.id}/inbound/items`,
{
items: [
{
id: order.items![0].id,
quantity: 1,
reason: ClaimReason.PRODUCTION_FAILURE,
},
],
},
adminHeaders
)
const shippingMethodResult = await api.post(
`/admin/claims/${claim.id}/inbound/shipping-method`,
{ shipping_option_id: returnShippingOption.id },
adminHeaders
)
const inboundShippingMethod =
shippingMethodResult.data.order_preview.shipping_methods.find(
(sm: any) => sm.shipping_option_id === returnShippingOption.id
)
expect(inboundShippingMethod).toEqual(
expect.objectContaining({
name: "Expédition de retour",
shipping_option_id: returnShippingOption.id,
})
)
})
it("should use original inbound shipping method name when order has no locale", async () => {
order = await createOrderWithLocale()
const claim = (
await api.post(
"/admin/claims",
{
order_id: order.id,
type: ClaimType.REPLACE,
description: "Test claim",
},
adminHeaders
)
).data.claim
await api.post(
`/admin/claims/${claim.id}/inbound/items`,
{
items: [
{
id: order.items![0].id,
quantity: 1,
reason: ClaimReason.PRODUCTION_FAILURE,
},
],
},
adminHeaders
)
const shippingMethodResult = await api.post(
`/admin/claims/${claim.id}/inbound/shipping-method`,
{ shipping_option_id: returnShippingOption.id },
adminHeaders
)
const inboundShippingMethod =
shippingMethodResult.data.order_preview.shipping_methods.find(
(sm: any) => sm.shipping_option_id === returnShippingOption.id
)
expect(inboundShippingMethod).toEqual(
expect.objectContaining({
name: "Return shipping",
shipping_option_id: returnShippingOption.id,
})
)
})
})
describe("Outbound shipping method", () => {
it("should translate outbound shipping method", async () => {
order = await createOrderWithLocale("fr-FR")
const claim = (
await api.post(
"/admin/claims",
{
order_id: order.id,
type: ClaimType.REPLACE,
description: "Test claim",
},
adminHeaders
)
).data.claim
await api.post(
`/admin/claims/${claim.id}/inbound/items`,
{
items: [
{
id: order.items![0].id,
quantity: 1,
reason: ClaimReason.PRODUCTION_FAILURE,
},
],
},
adminHeaders
)
await api.post(
`/admin/claims/${claim.id}/outbound/items`,
{
items: [
{
variant_id: productExtra.variants[0].id,
quantity: 1,
},
],
},
adminHeaders
)
const shippingMethodResult = await api.post(
`/admin/claims/${claim.id}/outbound/shipping-method`,
{ shipping_option_id: outboundShippingOption.id },
adminHeaders
)
const outboundShippingMethod =
shippingMethodResult.data.order_preview.shipping_methods.find(
(sm: any) => sm.shipping_option_id === outboundShippingOption.id
)
expect(outboundShippingMethod).toEqual(
expect.objectContaining({
name: "Expédition sortante",
shipping_option_id: outboundShippingOption.id,
})
)
})
it("should use original outbound shipping method name when order has no locale", async () => {
order = await createOrderWithLocale()
const claim = (
await api.post(
"/admin/claims",
{
order_id: order.id,
type: ClaimType.REPLACE,
description: "Test claim",
},
adminHeaders
)
).data.claim
await api.post(
`/admin/claims/${claim.id}/inbound/items`,
{
items: [
{
id: order.items![0].id,
quantity: 1,
reason: ClaimReason.PRODUCTION_FAILURE,
},
],
},
adminHeaders
)
await api.post(
`/admin/claims/${claim.id}/outbound/items`,
{
items: [
{
variant_id: productExtra.variants[0].id,
quantity: 1,
},
],
},
adminHeaders
)
const shippingMethodResult = await api.post(
`/admin/claims/${claim.id}/outbound/shipping-method`,
{ shipping_option_id: outboundShippingOption.id },
adminHeaders
)
const outboundShippingMethod =
shippingMethodResult.data.order_preview.shipping_methods.find(
(sm: any) => sm.shipping_option_id === outboundShippingOption.id
)
expect(outboundShippingMethod).toEqual(
expect.objectContaining({
name: "Outbound shipping",
shipping_option_id: outboundShippingOption.id,
})
)
})
})
describe("Both inbound and outbound shipping methods", () => {
it("should translate both shipping methods", async () => {
order = await createOrderWithLocale("fr-FR")
const claim = (
await api.post(
"/admin/claims",
{
order_id: order.id,
type: ClaimType.REPLACE,
description: "Test claim",
},
adminHeaders
)
).data.claim
await api.post(
`/admin/claims/${claim.id}/inbound/items`,
{
items: [
{
id: order.items![0].id,
quantity: 1,
reason: ClaimReason.PRODUCTION_FAILURE,
},
],
},
adminHeaders
)
await api.post(
`/admin/claims/${claim.id}/inbound/shipping-method`,
{ shipping_option_id: returnShippingOption.id },
adminHeaders
)
await api.post(
`/admin/claims/${claim.id}/outbound/items`,
{
items: [
{
variant_id: productExtra.variants[0].id,
quantity: 1,
},
],
},
adminHeaders
)
const result = await api.post(
`/admin/claims/${claim.id}/outbound/shipping-method`,
{ shipping_option_id: outboundShippingOption.id },
adminHeaders
)
const shippingMethods = result.data.order_preview.shipping_methods
const inboundShippingMethod = shippingMethods.find(
(sm: any) => sm.shipping_option_id === returnShippingOption.id
)
const outboundShippingMethod = shippingMethods.find(
(sm: any) => sm.shipping_option_id === outboundShippingOption.id
)
expect(inboundShippingMethod).toEqual(
expect.objectContaining({
name: "Expédition de retour",
shipping_option_id: returnShippingOption.id,
})
)
expect(outboundShippingMethod).toEqual(
expect.objectContaining({
name: "Expédition sortante",
shipping_option_id: outboundShippingOption.id,
})
)
})
it("should keep translations after confirming claim request", async () => {
order = await createOrderWithLocale("fr-FR")
const claim = (
await api.post(
"/admin/claims",
{
order_id: order.id,
type: ClaimType.REPLACE,
description: "Test claim",
},
adminHeaders
)
).data.claim
await api.post(
`/admin/claims/${claim.id}/inbound/items`,
{
items: [
{
id: order.items![0].id,
quantity: 1,
reason: ClaimReason.PRODUCTION_FAILURE,
},
],
},
adminHeaders
)
await api.post(
`/admin/claims/${claim.id}/inbound/shipping-method`,
{ shipping_option_id: returnShippingOption.id },
adminHeaders
)
await api.post(
`/admin/claims/${claim.id}/outbound/items`,
{
items: [
{
variant_id: productExtra.variants[0].id,
quantity: 1,
},
],
},
adminHeaders
)
await api.post(
`/admin/claims/${claim.id}/outbound/shipping-method`,
{ shipping_option_id: outboundShippingOption.id },
adminHeaders
)
await api.post(`/admin/claims/${claim.id}/request`, {}, adminHeaders)
const orderResult = await api.get(
`/admin/orders/${order.id}`,
adminHeaders
)
const shippingMethods = orderResult.data.order.shipping_methods
const outboundShippingMethod = shippingMethods.find(
(sm: any) => sm.shipping_option_id === outboundShippingOption.id
)
expect(outboundShippingMethod).toEqual(
expect.objectContaining({
name: "Expédition sortante",
shipping_option_id: outboundShippingOption.id,
})
)
})
})
})
},
})

View File

@@ -208,6 +208,22 @@ medusaIntegrationTestRunner({
locale_code: "de-DE",
translations: { title: "Mittel" },
},
{
reference_id: shippingOption.id,
reference: "shipping_option",
locale_code: "fr-FR",
translations: {
name: "Option d'expédition de test",
},
},
{
reference_id: shippingOption.id,
reference: "shipping_option",
locale_code: "de-DE",
translations: {
name: "Test-Versandoption",
},
},
],
},
adminHeaders
@@ -480,6 +496,202 @@ medusaIntegrationTestRunner({
)
})
})
describe("POST /admin/draft-orders/:id/edit/shipping-methods (add shipping method to draft order)", () => {
it("should translate shipping method added to draft order using draft order locale", async () => {
const draftOrder = (
await api.post(
"/admin/draft-orders",
{
email: "test@test.com",
region_id: region.id,
sales_channel_id: salesChannel.id,
locale: "fr-FR",
shipping_address: {
address_1: "123 Main St",
city: "Anytown",
country_code: "us",
postal_code: "12345",
first_name: "John",
},
},
adminHeaders
)
).data.draft_order
await api.post(
`/admin/draft-orders/${draftOrder.id}/edit`,
{},
adminHeaders
)
const previewResponse = await api.post(
`/admin/draft-orders/${draftOrder.id}/edit/shipping-methods`,
{ shipping_option_id: shippingOption.id },
adminHeaders
)
expect(
previewResponse.data.draft_order_preview.shipping_methods
).toEqual(
expect.arrayContaining([
expect.objectContaining({
shipping_option_id: shippingOption.id,
name: "Option d'expédition de test",
}),
])
)
await api.post(
`/admin/draft-orders/${draftOrder.id}/edit/confirm`,
{},
adminHeaders
)
const updatedDraftOrder = (
await api.get(`/admin/draft-orders/${draftOrder.id}`, adminHeaders)
).data.draft_order
expect(updatedDraftOrder.shipping_methods).toEqual(
expect.arrayContaining([
expect.objectContaining({
shipping_option_id: shippingOption.id,
name: "Option d'expédition de test",
}),
])
)
})
it("should have original shipping method name when draft order has no locale", async () => {
const draftOrder = (
await api.post(
"/admin/draft-orders",
{
email: "test@test.com",
region_id: region.id,
sales_channel_id: salesChannel.id,
shipping_address: {
address_1: "123 Main St",
city: "Anytown",
country_code: "us",
postal_code: "12345",
first_name: "John",
},
},
adminHeaders
)
).data.draft_order
await api.post(
`/admin/draft-orders/${draftOrder.id}/edit`,
{},
adminHeaders
)
const previewResponse = await api.post(
`/admin/draft-orders/${draftOrder.id}/edit/shipping-methods`,
{ shipping_option_id: shippingOption.id },
adminHeaders
)
expect(
previewResponse.data.draft_order_preview.shipping_methods
).toEqual(
expect.arrayContaining([
expect.objectContaining({
shipping_option_id: shippingOption.id,
name: "Test shipping option",
}),
])
)
await api.post(
`/admin/draft-orders/${draftOrder.id}/edit/confirm`,
{},
adminHeaders
)
const updatedDraftOrder = (
await api.get(`/admin/draft-orders/${draftOrder.id}`, adminHeaders)
).data.draft_order
expect(updatedDraftOrder.shipping_methods).toEqual(
expect.arrayContaining([
expect.objectContaining({
shipping_option_id: shippingOption.id,
name: "Test shipping option",
}),
])
)
})
})
describe("POST /admin/draft-orders/:id (update draft order locale)", () => {
it("should re-translate shipping methods when locale is updated", async () => {
const draftOrder = (
await api.post(
"/admin/draft-orders",
{
email: "test@test.com",
region_id: region.id,
sales_channel_id: salesChannel.id,
locale: "fr-FR",
shipping_address: {
address_1: "123 Main St",
city: "Anytown",
country_code: "us",
postal_code: "12345",
first_name: "John",
},
},
adminHeaders
)
).data.draft_order
await api.post(
`/admin/draft-orders/${draftOrder.id}/edit`,
{},
adminHeaders
)
await api.post(
`/admin/draft-orders/${draftOrder.id}/edit/shipping-methods`,
{ shipping_option_id: shippingOption.id },
adminHeaders
)
await api.post(
`/admin/draft-orders/${draftOrder.id}/edit/confirm`,
{},
adminHeaders
)
let updatedDraftOrder = (
await api.get(`/admin/draft-orders/${draftOrder.id}`, adminHeaders)
).data.draft_order
expect(updatedDraftOrder.shipping_methods[0].name).toEqual(
"Option d'expédition de test"
)
await api.post(
`/admin/draft-orders/${draftOrder.id}`,
{ locale: "de-DE" },
adminHeaders
)
updatedDraftOrder = (
await api.get(`/admin/draft-orders/${draftOrder.id}`, adminHeaders)
).data.draft_order
expect(updatedDraftOrder.shipping_methods[0]).toEqual(
expect.objectContaining({
shipping_option_id: shippingOption.id,
name: "Test-Versandoption",
})
)
})
})
})
},
})

View File

@@ -34,6 +34,7 @@ medusaIntegrationTestRunner({
let stockLocation: { id: string }
let shippingOption: { id: string }
let outboundShippingOption: { id: string }
let returnShippingOption: { id: string }
let inventoryItem: { id: string }
beforeAll(async () => {
@@ -224,6 +225,33 @@ medusaIntegrationTestRunner({
)
).data.shipping_option
returnShippingOption = (
await api.post(
`/admin/shipping-options`,
{
name: "Return shipping",
service_zone_id: fulfillmentSet.service_zones[0].id,
shipping_profile_id: shippingProfile.id,
provider_id: "manual_test-provider",
price_type: "flat",
type: {
label: "Test type",
description: "Test description",
code: "test-code",
},
prices: [{ currency_code: "usd", amount: 500 }],
rules: [
{
operator: RuleOperator.EQ,
attribute: "is_return",
value: "true",
},
],
},
adminHeaders
)
).data.shipping_option
await api.post(
"/admin/translations/batch",
{
@@ -270,6 +298,38 @@ medusaIntegrationTestRunner({
locale_code: "de-DE",
translations: { title: "Mittel" },
},
{
reference_id: outboundShippingOption.id,
reference: "shipping_option",
locale_code: "fr-FR",
translations: {
name: "Expédition sortante",
},
},
{
reference_id: outboundShippingOption.id,
reference: "shipping_option",
locale_code: "de-DE",
translations: {
name: "Ausgehende Versand",
},
},
{
reference_id: returnShippingOption.id,
reference: "shipping_option",
locale_code: "fr-FR",
translations: {
name: "Expédition de retour",
},
},
{
reference_id: returnShippingOption.id,
reference: "shipping_option",
locale_code: "de-DE",
translations: {
name: "Rückversand",
},
},
],
},
adminHeaders
@@ -522,6 +582,278 @@ medusaIntegrationTestRunner({
)
})
})
describe("Exchange shipping method translation", () => {
it("should translate outbound and inbound shipping methods added during exchange using order locale", async () => {
const order = await createOrderFromCart("fr-FR")
await api.post(
`/admin/orders/${order.id}/fulfillments`,
{
location_id: stockLocation.id,
items: [{ id: order.items[0].id, quantity: 1 }],
},
adminHeaders
)
const exchange = (
await api.post(
"/admin/exchanges",
{ order_id: order.id, description: "Test exchange" },
adminHeaders
)
).data.exchange
await api.post(
`/admin/exchanges/${exchange.id}/inbound/items`,
{
items: [{ id: order.items[0].id, quantity: 1 }],
},
adminHeaders
)
await api.post(
`/admin/exchanges/${exchange.id}/inbound/shipping-method`,
{ shipping_option_id: returnShippingOption.id },
adminHeaders
)
await api.post(
`/admin/exchanges/${exchange.id}/outbound/items`,
{
items: [{ variant_id: product.variants[1].id, quantity: 1 }],
},
adminHeaders
)
await api.post(
`/admin/exchanges/${exchange.id}/outbound/shipping-method`,
{ shipping_option_id: outboundShippingOption.id },
adminHeaders
)
const exchangeResult = (
await api.post(
`/admin/exchanges/${exchange.id}/request`,
{},
adminHeaders
)
).data.exchange
const updatedOrder = (
await api.get(`/admin/orders/${order.id}`, adminHeaders)
).data.order
const outboundShippingMethod = updatedOrder.shipping_methods.find(
(sm: any) => sm.shipping_option_id === outboundShippingOption.id
)
expect(outboundShippingMethod).toEqual(
expect.objectContaining({
shipping_option_id: outboundShippingOption.id,
name: "Expédition sortante",
})
)
const orderReturn = (
await api.get(
`/admin/returns/${exchangeResult.return_id}?fields=*shipping_methods`,
adminHeaders
)
).data.return
const inboundShippingMethod = orderReturn.shipping_methods.find(
(sm: any) => sm.shipping_option_id === returnShippingOption.id
)
expect(inboundShippingMethod).toEqual(
expect.objectContaining({
shipping_option_id: returnShippingOption.id,
name: "Expédition de retour",
})
)
})
it("should translate outbound and inbound shipping methods using German locale", async () => {
const order = await createOrderFromCart("de-DE")
await api.post(
`/admin/orders/${order.id}/fulfillments`,
{
location_id: stockLocation.id,
items: [{ id: order.items[0].id, quantity: 1 }],
},
adminHeaders
)
const exchange = (
await api.post(
"/admin/exchanges",
{ order_id: order.id, description: "Test exchange" },
adminHeaders
)
).data.exchange
await api.post(
`/admin/exchanges/${exchange.id}/inbound/items`,
{
items: [{ id: order.items[0].id, quantity: 1 }],
},
adminHeaders
)
await api.post(
`/admin/exchanges/${exchange.id}/inbound/shipping-method`,
{ shipping_option_id: returnShippingOption.id },
adminHeaders
)
await api.post(
`/admin/exchanges/${exchange.id}/outbound/items`,
{
items: [{ variant_id: product.variants[1].id, quantity: 1 }],
},
adminHeaders
)
await api.post(
`/admin/exchanges/${exchange.id}/outbound/shipping-method`,
{ shipping_option_id: outboundShippingOption.id },
adminHeaders
)
const exchangeResult = (
await api.post(
`/admin/exchanges/${exchange.id}/request`,
{},
adminHeaders
)
).data.exchange
const updatedOrder = (
await api.get(`/admin/orders/${order.id}`, adminHeaders)
).data.order
const outboundShippingMethod = updatedOrder.shipping_methods.find(
(sm: any) => sm.shipping_option_id === outboundShippingOption.id
)
expect(outboundShippingMethod).toEqual(
expect.objectContaining({
shipping_option_id: outboundShippingOption.id,
name: "Ausgehende Versand",
})
)
const orderReturn = (
await api.get(
`/admin/returns/${exchangeResult.return_id}?fields=*shipping_methods`,
adminHeaders
)
).data.return
const inboundShippingMethod = orderReturn.shipping_methods.find(
(sm: any) => sm.shipping_option_id === returnShippingOption.id
)
expect(inboundShippingMethod).toEqual(
expect.objectContaining({
shipping_option_id: returnShippingOption.id,
name: "Rückversand",
})
)
})
it("should have original shipping method names when order has no locale", async () => {
const order = await createOrderFromCart()
await api.post(
`/admin/orders/${order.id}/fulfillments`,
{
location_id: stockLocation.id,
items: [{ id: order.items[0].id, quantity: 1 }],
},
adminHeaders
)
const exchange = (
await api.post(
"/admin/exchanges",
{ order_id: order.id, description: "Test exchange" },
adminHeaders
)
).data.exchange
await api.post(
`/admin/exchanges/${exchange.id}/inbound/items`,
{
items: [{ id: order.items[0].id, quantity: 1 }],
},
adminHeaders
)
await api.post(
`/admin/exchanges/${exchange.id}/inbound/shipping-method`,
{ shipping_option_id: returnShippingOption.id },
adminHeaders
)
await api.post(
`/admin/exchanges/${exchange.id}/outbound/items`,
{
items: [{ variant_id: product.variants[1].id, quantity: 1 }],
},
adminHeaders
)
await api.post(
`/admin/exchanges/${exchange.id}/outbound/shipping-method`,
{ shipping_option_id: outboundShippingOption.id },
adminHeaders
)
const exchangeResult = (
await api.post(
`/admin/exchanges/${exchange.id}/request`,
{},
adminHeaders
)
).data.exchange
const updatedOrder = (
await api.get(`/admin/orders/${order.id}`, adminHeaders)
).data.order
const outboundShippingMethod = updatedOrder.shipping_methods.find(
(sm: any) => sm.shipping_option_id === outboundShippingOption.id
)
expect(outboundShippingMethod).toEqual(
expect.objectContaining({
shipping_option_id: outboundShippingOption.id,
name: "Outbound shipping",
})
)
const orderReturn = (
await api.get(
`/admin/returns/${exchangeResult.return_id}?fields=*shipping_methods`,
adminHeaders
)
).data.return
const inboundShippingMethod = orderReturn.shipping_methods.find(
(sm: any) => sm.shipping_option_id === returnShippingOption.id
)
expect(inboundShippingMethod).toEqual(
expect.objectContaining({
shipping_option_id: returnShippingOption.id,
name: "Return shipping",
})
)
})
})
})
},
})

View File

@@ -33,6 +33,7 @@ medusaIntegrationTestRunner({
let shippingProfile: { id: string }
let stockLocation: { id: string }
let shippingOption: { id: string }
let additionalShippingOption: { id: string }
let inventoryItem: { id: string }
beforeAll(async () => {
@@ -196,6 +197,27 @@ medusaIntegrationTestRunner({
)
).data.shipping_option
additionalShippingOption = (
await api.post(
`/admin/shipping-options`,
{
name: "Additional shipping option",
service_zone_id: fulfillmentSet.service_zones[0].id,
shipping_profile_id: shippingProfile.id,
provider_id: "manual_test-provider",
price_type: "flat",
type: {
label: "Test type",
description: "Test description",
code: "test-code",
},
prices: [{ currency_code: "usd", amount: 500 }],
rules: [],
},
adminHeaders
)
).data.shipping_option
await api.post(
"/admin/translations/batch",
{
@@ -242,6 +264,38 @@ medusaIntegrationTestRunner({
locale_code: "de-DE",
translations: { title: "Mittel" },
},
{
reference_id: shippingOption.id,
reference: "shipping_option",
locale_code: "fr-FR",
translations: {
name: "Option d'expédition de test",
},
},
{
reference_id: shippingOption.id,
reference: "shipping_option",
locale_code: "de-DE",
translations: {
name: "Test-Versandoption",
},
},
{
reference_id: additionalShippingOption.id,
reference: "shipping_option",
locale_code: "fr-FR",
translations: {
name: "Option d'expédition supplémentaire",
},
},
{
reference_id: additionalShippingOption.id,
reference: "shipping_option",
locale_code: "de-DE",
translations: {
name: "Zusätzliche Versandoption",
},
},
],
},
adminHeaders
@@ -414,6 +468,96 @@ medusaIntegrationTestRunner({
)
})
})
describe("POST /admin/order-edits/:id/shipping-method (add shipping method during order edit)", () => {
it("should translate shipping method added during order edit using order locale", async () => {
const order = await createOrderFromCart("fr-FR")
await api.post(
"/admin/order-edits",
{ order_id: order.id },
adminHeaders
)
const previewResponse = await api.post(
`/admin/order-edits/${order.id}/shipping-method`,
{ shipping_option_id: additionalShippingOption.id },
adminHeaders
)
expect(previewResponse.data.order_preview.shipping_methods).toEqual(
expect.arrayContaining([
expect.objectContaining({
shipping_option_id: additionalShippingOption.id,
name: "Option d'expédition supplémentaire",
}),
])
)
await api.post(
`/admin/order-edits/${order.id}/confirm`,
{},
adminHeaders
)
const updatedOrder = (
await api.get(`/admin/orders/${order.id}`, adminHeaders)
).data.order
expect(updatedOrder.shipping_methods).toEqual(
expect.arrayContaining([
expect.objectContaining({
shipping_option_id: additionalShippingOption.id,
name: "Option d'expédition supplémentaire",
}),
])
)
})
it("should have original shipping method name when order has no locale", async () => {
const order = await createOrderFromCart()
await api.post(
"/admin/order-edits",
{ order_id: order.id },
adminHeaders
)
const previewResponse = await api.post(
`/admin/order-edits/${order.id}/shipping-method`,
{ shipping_option_id: additionalShippingOption.id },
adminHeaders
)
expect(previewResponse.data.order_preview.shipping_methods).toEqual(
expect.arrayContaining([
expect.objectContaining({
shipping_option_id: additionalShippingOption.id,
name: "Additional shipping option",
}),
])
)
await api.post(
`/admin/order-edits/${order.id}/confirm`,
{},
adminHeaders
)
const updatedOrder = (
await api.get(`/admin/orders/${order.id}`, adminHeaders)
).data.order
expect(updatedOrder.shipping_methods).toEqual(
expect.arrayContaining([
expect.objectContaining({
shipping_option_id: additionalShippingOption.id,
name: "Additional shipping option",
}),
])
)
})
})
})
},
})

View File

@@ -255,7 +255,7 @@ medusaIntegrationTestRunner({
)
).data.shipping_option
// Create translations for product and variants
// Create translations for product, variants, and shipping options
await api.post(
"/admin/translations/batch",
{
@@ -302,6 +302,22 @@ medusaIntegrationTestRunner({
locale_code: "de-DE",
translations: { title: "Mittel" },
},
{
reference_id: shippingOption.id,
reference: "shipping_option",
locale_code: "fr-FR",
translations: {
name: "Option d'expédition de test",
},
},
{
reference_id: shippingOption.id,
reference: "shipping_option",
locale_code: "de-DE",
translations: {
name: "Test-Versandoption",
},
},
],
},
adminHeaders
@@ -378,6 +394,30 @@ medusaIntegrationTestRunner({
})
)
})
it("should preserve translated shipping methods when order is created from cart with locale", async () => {
const order = await createOrderFromCart("fr-FR")
expect(order.shipping_methods).toHaveLength(1)
expect(order.shipping_methods[0]).toEqual(
expect.objectContaining({
shipping_option_id: shippingOption.id,
name: "Option d'expédition de test",
})
)
})
it("should have original shipping method name when order is created without locale", async () => {
const order = await createOrderFromCart()
expect(order.shipping_methods).toHaveLength(1)
expect(order.shipping_methods[0]).toEqual(
expect.objectContaining({
shipping_option_id: shippingOption.id,
name: "Test shipping option",
})
)
})
})
describe("POST /admin/orders/:id (update order locale)", () => {
@@ -405,6 +445,32 @@ medusaIntegrationTestRunner({
)
})
it("should re-translate shipping methods when locale is updated", async () => {
const order = await createOrderFromCart("fr-FR")
expect(order.shipping_methods[0].name).toEqual(
"Option d'expédition de test"
)
await api.post(
`/admin/orders/${order.id}`,
{ locale: "de-DE" },
adminHeaders
)
const updatedOrder = (
await api.get(`/admin/orders/${order.id}`, adminHeaders)
).data.order
expect(updatedOrder.shipping_methods).toHaveLength(1)
expect(updatedOrder.shipping_methods[0]).toEqual(
expect.objectContaining({
shipping_option_id: shippingOption.id,
name: "Test-Versandoption",
})
)
})
it("should not re-translate items when updating other fields", async () => {
const order = await createOrderFromCart("fr-FR")
@@ -429,6 +495,31 @@ medusaIntegrationTestRunner({
})
)
})
it("should not re-translate shipping methods when updating other fields", async () => {
const order = await createOrderFromCart("fr-FR")
await api.post(
`/admin/orders/${order.id}`,
{ email: "updated@example.com" },
adminHeaders
)
const updatedOrder = (
await api.get(
`/admin/orders/${order.id}?fields=+email,+shipping_methods.name`,
adminHeaders
)
).data.order
expect(updatedOrder.email).toEqual("updated@example.com")
expect(updatedOrder.shipping_methods[0]).toEqual(
expect.objectContaining({
shipping_option_id: shippingOption.id,
name: "Option d'expédition de test",
})
)
})
})
})
},

View File

@@ -0,0 +1,539 @@
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
import { Modules, RuleOperator } from "@medusajs/utils"
import {
adminHeaders,
createAdminUser,
} from "../../../helpers/create-admin-user"
jest.setTimeout(60000)
process.env.MEDUSA_FF_TRANSLATION = "true"
medusaIntegrationTestRunner({
testSuite: ({ dbConnection, getContainer, api }) => {
let order
let returnShippingOption
let shippingProfile
let fulfillmentSet
let inventoryItem
let location
let salesChannel
let product
beforeEach(async () => {
const container = getContainer()
await createAdminUser(dbConnection, adminHeaders, container)
// Set up supported locales in the store
const storeModule = container.resolve(Modules.STORE)
const [defaultStore] = await storeModule.listStores(
{},
{ select: ["id"], take: 1 }
)
await storeModule.updateStores(defaultStore.id, {
supported_locales: [
{ locale_code: "en-US" },
{ locale_code: "fr-FR" },
{ locale_code: "de-DE" },
],
})
salesChannel = (
await api.post(
"/admin/sales-channels",
{ name: "Webshop", description: "channel" },
adminHeaders
)
).data.sales_channel
shippingProfile = (
await api.post(
`/admin/shipping-profiles`,
{
name: "Test",
type: "default",
},
adminHeaders
)
).data.shipping_profile
location = (
await api.post(
`/admin/stock-locations`,
{
name: "Test location",
},
adminHeaders
)
).data.stock_location
inventoryItem = (
await api.post(
`/admin/inventory-items`,
{ sku: "inv-1234" },
adminHeaders
)
).data.inventory_item
await api.post(
`/admin/inventory-items/${inventoryItem.id}/location-levels`,
{
location_id: location.id,
stocked_quantity: 2,
},
adminHeaders
)
await api.post(
`/admin/stock-locations/${location.id}/sales-channels`,
{ add: [salesChannel.id] },
adminHeaders
)
product = (
await api.post(
"/admin/products",
{
title: "Test product",
options: [{ title: "size", values: ["x", "l"] }],
shipping_profile_id: shippingProfile.id,
variants: [
{
title: "Test variant",
sku: "test-variant",
options: { size: "l" },
inventory_items: [
{
inventory_item_id: inventoryItem.id,
required_quantity: 1,
},
],
prices: [
{
currency_code: "usd",
amount: 10,
},
],
},
],
},
adminHeaders
)
).data.product
location = (
await api.post(
`/admin/stock-locations/${location.id}/fulfillment-sets?fields=*fulfillment_sets`,
{
name: "Test",
type: "test-type",
},
adminHeaders
)
).data.stock_location
fulfillmentSet = (
await api.post(
`/admin/fulfillment-sets/${location.fulfillment_sets[0].id}/service-zones`,
{
name: "Test",
geo_zones: [{ type: "country", country_code: "us" }],
},
adminHeaders
)
).data.fulfillment_set
await api.post(
`/admin/stock-locations/${location.id}/fulfillment-providers`,
{ add: ["manual_test-provider"] },
adminHeaders
)
returnShippingOption = (
await api.post(
"/admin/shipping-options",
{
name: "Return shipping",
service_zone_id: fulfillmentSet.service_zones[0].id,
shipping_profile_id: shippingProfile.id,
provider_id: "manual_test-provider",
price_type: "flat",
type: {
label: "Test type",
description: "Test description",
code: "test-code",
},
prices: [
{
currency_code: "usd",
amount: 1000,
},
],
rules: [
{
operator: RuleOperator.EQ,
attribute: "is_return",
value: "true",
},
],
},
adminHeaders
)
).data.shipping_option
// Create translations for the return shipping option
await api.post(
"/admin/translations/batch",
{
create: [
{
reference_id: returnShippingOption.id,
reference: "shipping_option",
locale_code: "fr-FR",
translations: {
name: "Expédition de retour",
},
},
{
reference_id: returnShippingOption.id,
reference: "shipping_option",
locale_code: "de-DE",
translations: {
name: "Rückversand",
},
},
],
},
adminHeaders
)
})
const createOrderWithLocale = async (locale?: string) => {
const container = getContainer()
const orderModule = container.resolve(Modules.ORDER)
const inventoryModule = container.resolve(Modules.INVENTORY)
const createdOrder = await orderModule.createOrders({
region_id: "test_region_id",
email: "foo@bar.com",
locale,
items: [
{
title: "Custom Item",
variant_id: product.variants[0].id,
quantity: 2,
unit_price: 25,
},
],
sales_channel_id: salesChannel.id,
shipping_address: {
first_name: "Test",
last_name: "Test",
address_1: "Test",
city: "Test",
country_code: "US",
postal_code: "12345",
phone: "12345",
},
billing_address: {
first_name: "Test",
last_name: "Test",
address_1: "Test",
city: "Test",
country_code: "US",
postal_code: "12345",
},
shipping_methods: [
{
name: "Test shipping method",
amount: 10,
data: {},
},
],
currency_code: "usd",
customer_id: "joe",
})
await inventoryModule.createReservationItems([
{
inventory_item_id: inventoryItem.id,
location_id: location.id,
quantity: 2,
line_item_id: createdOrder.items![0].id,
},
])
// Fulfill the order
await api.post(
`/admin/orders/${createdOrder.id}/fulfillments`,
{
items: [
{
id: createdOrder.items![0].id,
quantity: 2,
},
],
},
adminHeaders
)
return createdOrder
}
describe("Return shipping method translation", () => {
it("should translate return shipping method name using French locale", async () => {
order = await createOrderWithLocale("fr-FR")
const returnResult = await api.post(
"/admin/returns",
{
order_id: order.id,
location_id: location.id,
},
adminHeaders
)
const returnId = returnResult.data.return.id
const item = order.items[0]
await api.post(
`/admin/returns/${returnId}/request-items`,
{
items: [
{
id: item.id,
quantity: 1,
},
],
},
adminHeaders
)
const shippingMethodResult = await api.post(
`/admin/returns/${returnId}/shipping-method`,
{
shipping_option_id: returnShippingOption.id,
},
adminHeaders
)
const returnShippingMethod =
shippingMethodResult.data.order_preview.shipping_methods.find(
(sm: any) => sm.shipping_option_id === returnShippingOption.id
)
expect(returnShippingMethod).toEqual(
expect.objectContaining({
name: "Expédition de retour",
shipping_option_id: returnShippingOption.id,
})
)
})
it("should use original shipping method name when order has no locale", async () => {
order = await createOrderWithLocale()
const returnResult = await api.post(
"/admin/returns",
{
order_id: order.id,
location_id: location.id,
},
adminHeaders
)
const returnId = returnResult.data.return.id
const item = order.items[0]
await api.post(
`/admin/returns/${returnId}/request-items`,
{
items: [
{
id: item.id,
quantity: 1,
},
],
},
adminHeaders
)
const shippingMethodResult = await api.post(
`/admin/returns/${returnId}/shipping-method`,
{
shipping_option_id: returnShippingOption.id,
},
adminHeaders
)
const returnShippingMethod =
shippingMethodResult.data.order_preview.shipping_methods.find(
(sm: any) => sm.shipping_option_id === returnShippingOption.id
)
expect(returnShippingMethod).toEqual(
expect.objectContaining({
name: "Return shipping",
shipping_option_id: returnShippingOption.id,
})
)
})
it("should use original name when locale has no translation", async () => {
order = await createOrderWithLocale("en-US")
const returnResult = await api.post(
"/admin/returns",
{
order_id: order.id,
location_id: location.id,
},
adminHeaders
)
const returnId = returnResult.data.return.id
const item = order.items[0]
await api.post(
`/admin/returns/${returnId}/request-items`,
{
items: [
{
id: item.id,
quantity: 1,
},
],
},
adminHeaders
)
const shippingMethodResult = await api.post(
`/admin/returns/${returnId}/shipping-method`,
{
shipping_option_id: returnShippingOption.id,
},
adminHeaders
)
const returnShippingMethod =
shippingMethodResult.data.order_preview.shipping_methods.find(
(sm: any) => sm.shipping_option_id === returnShippingOption.id
)
expect(returnShippingMethod).toEqual(
expect.objectContaining({
name: "Return shipping",
shipping_option_id: returnShippingOption.id,
})
)
})
it("should translate return shipping method with custom price", async () => {
order = await createOrderWithLocale("fr-FR")
const returnResult = await api.post(
"/admin/returns",
{
order_id: order.id,
location_id: location.id,
},
adminHeaders
)
const returnId = returnResult.data.return.id
const item = order.items[0]
await api.post(
`/admin/returns/${returnId}/request-items`,
{
items: [
{
id: item.id,
quantity: 1,
},
],
},
adminHeaders
)
const shippingMethodResult = await api.post(
`/admin/returns/${returnId}/shipping-method`,
{
shipping_option_id: returnShippingOption.id,
custom_amount: 500,
},
adminHeaders
)
const returnShippingMethod =
shippingMethodResult.data.order_preview.shipping_methods.find(
(sm: any) => sm.shipping_option_id === returnShippingOption.id
)
expect(returnShippingMethod).toEqual(
expect.objectContaining({
name: "Expédition de retour",
shipping_option_id: returnShippingOption.id,
amount: 500,
})
)
})
it("should keep translation after confirming return request", async () => {
order = await createOrderWithLocale("fr-FR")
const returnResult = await api.post(
"/admin/returns",
{
order_id: order.id,
location_id: location.id,
},
adminHeaders
)
const returnId = returnResult.data.return.id
const item = order.items[0]
await api.post(
`/admin/returns/${returnId}/request-items`,
{
items: [
{
id: item.id,
quantity: 1,
},
],
},
adminHeaders
)
await api.post(
`/admin/returns/${returnId}/shipping-method`,
{
shipping_option_id: returnShippingOption.id,
},
adminHeaders
)
await api.post(`/admin/returns/${returnId}/request`, {}, adminHeaders)
const orderResult = await api.get(
`/admin/orders/${order.id}`,
adminHeaders
)
const returnShippingMethod =
orderResult.data.order.shipping_methods.find(
(sm: any) => sm.shipping_option_id === returnShippingOption.id
)
expect(returnShippingMethod).toEqual(
expect.objectContaining({
name: "Expédition de retour",
shipping_option_id: returnShippingOption.id,
})
)
})
})
},
})

View File

@@ -0,0 +1,344 @@
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
import {
createAdminUser,
generatePublishableKey,
generateStoreHeaders,
} from "../../../../helpers/create-admin-user"
import { Modules, ProductStatus } from "@medusajs/utils"
jest.setTimeout(50000)
process.env.MEDUSA_FF_TRANSLATION = "true"
const env = {}
const adminHeaders = { headers: { "x-medusa-access-token": "test_token" } }
medusaIntegrationTestRunner({
env,
testSuite: ({ dbConnection, getContainer, api }) => {
describe("Store: Shipping Option API (with translations)", () => {
let appContainer
let salesChannel
let region
let product
let stockLocation
let shippingProfile
let fulfillmentSet
let shippingOption
let storeHeaders
beforeAll(async () => {
appContainer = getContainer()
})
afterAll(async () => {
delete process.env.MEDUSA_FF_TRANSLATION
})
beforeEach(async () => {
const publishableKey = await generatePublishableKey(appContainer)
storeHeaders = generateStoreHeaders({ publishableKey })
await createAdminUser(dbConnection, adminHeaders, appContainer)
// Set up store locales
const storeModule = appContainer.resolve(Modules.STORE)
const [defaultStore] = await storeModule.listStores(
{},
{
select: ["id"],
take: 1,
}
)
await storeModule.updateStores(defaultStore.id, {
supported_locales: [
{ locale_code: "en-US" },
{ locale_code: "fr-FR" },
{ locale_code: "de-DE" },
],
})
region = (
await api.post(
"/admin/regions",
{ name: "US", currency_code: "usd", countries: ["US"] },
adminHeaders
)
).data.region
shippingProfile = (
await api.post(
`/admin/shipping-profiles`,
{ name: "Test", type: "default" },
adminHeaders
)
).data.shipping_profile
salesChannel = (
await api.post(
"/admin/sales-channels",
{ name: "first channel", description: "channel" },
adminHeaders
)
).data.sales_channel
product = (
await api.post(
"/admin/products",
{
title: "Test fixture",
status: ProductStatus.PUBLISHED,
options: [
{ title: "size", values: ["large", "small"] },
{ title: "color", values: ["green"] },
],
shipping_profile_id: shippingProfile.id,
variants: [
{
title: "Test variant",
manage_inventory: false,
prices: [
{
currency_code: "usd",
amount: 100,
},
],
options: {
size: "large",
color: "green",
},
},
],
},
adminHeaders
)
).data.product
stockLocation = (
await api.post(
`/admin/stock-locations`,
{ name: "test location" },
adminHeaders
)
).data.stock_location
await api.post(
`/admin/stock-locations/${stockLocation.id}/sales-channels`,
{ add: [salesChannel.id] },
adminHeaders
)
const fulfillmentSets = (
await api.post(
`/admin/stock-locations/${stockLocation.id}/fulfillment-sets?fields=*fulfillment_sets`,
{
name: "Test",
type: "test-type",
},
adminHeaders
)
).data.stock_location.fulfillment_sets
fulfillmentSet = (
await api.post(
`/admin/fulfillment-sets/${fulfillmentSets[0].id}/service-zones`,
{
name: "Test",
geo_zones: [
{ type: "country", country_code: "us" },
{ type: "country", country_code: "dk" },
],
},
adminHeaders
)
).data.fulfillment_set
await api.post(
`/admin/stock-locations/${stockLocation.id}/fulfillment-providers`,
{ add: ["manual_test-provider"] },
adminHeaders
)
shippingOption = (
await api.post(
`/admin/shipping-options`,
{
name: "Test shipping option",
service_zone_id: fulfillmentSet.service_zones[0].id,
shipping_profile_id: shippingProfile.id,
provider_id: "manual_test-provider",
price_type: "flat",
type: {
label: "Test type",
description: "Test description",
code: "test-code",
},
prices: [
{
currency_code: "usd",
amount: 1000,
},
{
region_id: region.id,
amount: 1100,
},
],
rules: [],
},
adminHeaders
)
).data.shipping_option
// Create translations for shipping option
await api.post(
"/admin/translations/batch",
{
create: [
{
reference_id: shippingOption.id,
reference: "shipping_option",
locale_code: "fr-FR",
translations: {
name: "Option d'expédition test",
},
},
{
reference_id: shippingOption.id,
reference: "shipping_option",
locale_code: "de-DE",
translations: {
name: "Test-Versandoption",
},
},
],
},
adminHeaders
)
})
describe("GET /store/shipping-options?cart_id=", () => {
it("should return translated shipping options when cart has locale", async () => {
const cartResponse = await api.post(
`/store/carts`,
{
region_id: region.id,
sales_channel_id: salesChannel.id,
currency_code: "usd",
locale: "fr-FR",
email: "test@admin.com",
items: [
{
variant_id: product.variants[0].id,
quantity: 1,
},
],
},
storeHeaders
)
const cart = cartResponse.data.cart
const shippingOptionsResponse = await api.get(
`/store/shipping-options?cart_id=${cart.id}`,
storeHeaders
)
expect(shippingOptionsResponse.status).toEqual(200)
expect(shippingOptionsResponse.data.shipping_options).toHaveLength(1)
expect(shippingOptionsResponse.data.shipping_options[0]).toEqual(
expect.objectContaining({
id: shippingOption.id,
name: "Option d'expédition test",
})
)
})
it("should return translated shipping options when locale is changed", async () => {
const cartResponse = await api.post(
`/store/carts`,
{
region_id: region.id,
sales_channel_id: salesChannel.id,
currency_code: "usd",
locale: "fr-FR",
email: "test@admin.com",
items: [
{
variant_id: product.variants[0].id,
quantity: 1,
},
],
},
storeHeaders
)
const cart = cartResponse.data.cart
// Verify French translation
let shippingOptionsResponse = await api.get(
`/store/shipping-options?cart_id=${cart.id}`,
storeHeaders
)
expect(shippingOptionsResponse.data.shipping_options[0].name).toEqual(
"Option d'expédition test"
)
// Update cart locale to German
await api.post(
`/store/carts/${cart.id}`,
{
locale: "de-DE",
},
storeHeaders
)
// Verify German translation
shippingOptionsResponse = await api.get(
`/store/shipping-options?cart_id=${cart.id}`,
storeHeaders
)
expect(shippingOptionsResponse.data.shipping_options[0].name).toEqual(
"Test-Versandoption"
)
})
it("should return original shipping option name when no translation exists", async () => {
const cartResponse = await api.post(
`/store/carts`,
{
region_id: region.id,
sales_channel_id: salesChannel.id,
currency_code: "usd",
locale: "ja-JP",
email: "test@admin.com",
items: [
{
variant_id: product.variants[0].id,
quantity: 1,
},
],
},
storeHeaders
)
const cart = cartResponse.data.cart
const shippingOptionsResponse = await api.get(
`/store/shipping-options?cart_id=${cart.id}`,
storeHeaders
)
expect(shippingOptionsResponse.status).toEqual(200)
expect(shippingOptionsResponse.data.shipping_options[0]).toEqual(
expect.objectContaining({
id: shippingOption.id,
name: "Test shipping option",
})
)
})
})
})
},
})