@faststore/core 4.8.0 → 4.8.1-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/.turbo/turbo-generate.log +3 -3
  2. package/.turbo/turbo-test.log +146 -108
  3. package/CHANGELOG.md +14 -0
  4. package/cms/faststore/my-account/components/cms_component__accountnavigation.jsonc +10 -0
  5. package/cms/faststore/my-account/components/cms_component__accountorderdelivery.jsonc +80 -0
  6. package/cms/faststore/my-account/components/cms_component__accountorderdetails.jsonc +25 -0
  7. package/cms/faststore/my-account/components/cms_component__accountorderslist.jsonc +10 -0
  8. package/cms/faststore/my-account/components/cms_component__accountorderstatus.jsonc +90 -0
  9. package/cms/faststore/my-account/components/cms_component__accountordersummary.jsonc +25 -0
  10. package/package.json +6 -6
  11. package/src/components/account/Drawer/OrganizationDrawer/OrganizationDrawerFooter.tsx +13 -2
  12. package/src/components/account/orders/ListOrders/FilterSlider/FilterFacetPendingApproval/FilterFacetPendingApproval.tsx +8 -3
  13. package/src/components/account/orders/ListOrders/FilterSlider/FilterSlider.tsx +1 -0
  14. package/src/components/account/orders/ListOrders/ListOrders.tsx +2 -2
  15. package/src/components/account/orders/ListOrders/listOrdersLabels.ts +4 -0
  16. package/src/components/account/orders/OrderDetails/BuyingPolicyAlert/BuyingPolicyAlert.tsx +45 -9
  17. package/src/components/account/orders/OrderDetails/DeliveryCard/DeliveryCard.tsx +14 -3
  18. package/src/components/account/orders/OrderDetails/DeliveryOptionAccordion/DeliveryOptionAccordion.tsx +4 -1
  19. package/src/components/account/orders/OrderDetails/DeliveryOptionAccordion/DeliveryOptionAccordionDeliveryInfo.tsx +14 -7
  20. package/src/components/account/orders/OrderDetails/DeliveryOptionAccordion/DeliveryOptionAccordionProducts.tsx +8 -5
  21. package/src/components/account/orders/OrderDetails/OrderDetailsHeader.tsx +5 -0
  22. package/src/components/account/orders/OrderDetails/StatusCard/StatusCard.tsx +26 -89
  23. package/src/components/account/orders/OrderDetails/StatusCard/statusCardLocalization.ts +114 -0
  24. package/src/components/account/orders/OrderDetails/SummaryCard/SummaryCard.tsx +2 -1
  25. package/src/components/account/orders/OrderDetails/SummaryCard/getOrderTotalLabel.ts +19 -0
  26. package/src/components/account/orders/OrderDetails/getDeliveryOptionLabel.ts +135 -0
  27. package/src/components/account/orders/OrderDetails/orderDetailsLabels.ts +73 -0
  28. package/src/components/sections/Account/AccountOrderDelivery.tsx +3 -1
  29. package/src/components/sections/Account/AccountOrderDetails.tsx +16 -0
  30. package/src/components/sections/Account/AccountOrderStatus.tsx +27 -3
  31. package/src/components/sections/Account/AccountOrdersList.tsx +4 -0
  32. package/src/pages/api/graphql.ts +108 -69
  33. package/src/sdk/account/accountPageContext.ts +14 -3
  34. package/src/sdk/account/getMyAccountRoutes.ts +2 -0
  35. package/src/sdk/overrides/PageProvider.tsx +1 -1
  36. package/test/components/account/OrganizationDrawerFooter.browser.test.tsx +95 -1
  37. package/test/components/account/orders/BuyingPolicyAlert.browser.test.tsx +342 -0
  38. package/test/components/account/orders/DeliveryCard.browser.test.tsx +36 -0
  39. package/test/components/account/orders/DeliveryOptionAccordion.browser.test.tsx +76 -0
  40. package/test/components/account/orders/DeliveryOptionAccordionDeliveryInfo.browser.test.tsx +41 -0
  41. package/test/components/account/orders/DeliveryOptionAccordionProducts.browser.test.tsx +35 -0
  42. package/test/components/account/orders/FilterFacetPendingApproval.browser.test.tsx +25 -0
  43. package/test/components/account/orders/StatusCard.browser.test.tsx +37 -0
  44. package/test/components/account/orders/StatusCard.test.ts +101 -0
  45. package/test/components/account/orders/SummaryCard.browser.test.tsx +59 -0
  46. package/test/components/account/orders/getDeliveryOptionLabel.test.ts +129 -0
  47. package/test/components/account/orders/getOrderTotalLabel.test.ts +35 -0
  48. package/test/components/account/orders/listOrdersLabels.test.ts +23 -0
  49. package/test/components/account/orders/orderDetailsLabels.test.ts +13 -0
  50. package/test/components/sections/Account/AccountOrderDelivery.browser.test.tsx +72 -0
  51. package/test/components/sections/Account/AccountOrderDetails.browser.test.tsx +151 -0
  52. package/test/components/sections/Account/AccountOrderStatus.browser.test.tsx +59 -0
  53. package/test/components/sections/Account/AccountOrdersList.browser.test.tsx +67 -0
  54. package/test/pages/api/graphql.test.ts +27 -1
  55. package/test/sdk/account/getMyAccountRoutes.test.ts +11 -1
  56. package/test/utils/userOrderStatus.test.ts +22 -0
@@ -0,0 +1,59 @@
1
+ /**
2
+ * @vitest-environment jsdom
3
+ */
4
+
5
+ import { render, screen } from '@testing-library/react'
6
+ import { describe, expect, it, vi } from 'vitest'
7
+
8
+ import SummaryCard from '../../../../src/components/account/orders/OrderDetails/SummaryCard'
9
+
10
+ vi.mock('next/router', () => ({
11
+ useRouter: () => ({ locale: 'en-US' }),
12
+ }))
13
+
14
+ describe('SummaryCard', () => {
15
+ it('renders localized totals, interest, and the final total', () => {
16
+ render(
17
+ <SummaryCard
18
+ totals={[
19
+ { id: 'Items', name: 'Items from OMS', value: 1_000 },
20
+ { id: 'FutureTotal', name: 'Future total from OMS', value: 100 },
21
+ ]}
22
+ currencyCode="USD"
23
+ transactions={[
24
+ {
25
+ isActive: true,
26
+ payments: [{ value: 1_200, referenceValue: 1_100 }],
27
+ },
28
+ ]}
29
+ labels={{
30
+ itemsTotalLabel: 'Products',
31
+ interestLabel: 'Financing',
32
+ totalLabel: 'Grand total',
33
+ }}
34
+ />
35
+ )
36
+
37
+ expect(screen.getByText('Products')).toBeTruthy()
38
+ expect(screen.getByText('Future total from OMS')).toBeTruthy()
39
+ expect(screen.getByText('Financing')).toBeTruthy()
40
+ expect(screen.getByText('Grand total')).toBeTruthy()
41
+ })
42
+
43
+ it('does not add interest for inactive transactions', () => {
44
+ render(
45
+ <SummaryCard
46
+ totals={[{ id: 'Items', name: 'Items from OMS', value: 1_000 }]}
47
+ currencyCode="USD"
48
+ transactions={[
49
+ {
50
+ isActive: false,
51
+ payments: [{ value: 1_200, referenceValue: 1_000 }],
52
+ },
53
+ ]}
54
+ />
55
+ )
56
+
57
+ expect(screen.queryByText('Interest')).toBeNull()
58
+ })
59
+ })
@@ -0,0 +1,129 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import {
4
+ getDeliveryOptionKey,
5
+ getDeliveryOptionLabel,
6
+ } from '../../../../src/components/account/orders/OrderDetails/getDeliveryOptionLabel'
7
+ import { resolveOrderDeliveryLabels } from '../../../../src/components/account/orders/OrderDetails/orderDetailsLabels'
8
+
9
+ const labels = resolveOrderDeliveryLabels({
10
+ deliveryChannelLabel: 'Entrega',
11
+ pickupChannelLabel: 'Retirada',
12
+ takeAwayChannelLabel: 'Levar',
13
+ businessDaysZeroTemplate: 'bd-0',
14
+ businessDaysOneTemplate: 'bd-1',
15
+ businessDaysOtherTemplate: 'bd-{count}',
16
+ daysZeroTemplate: 'd-0',
17
+ daysOneTemplate: 'd-1',
18
+ daysOtherTemplate: 'd-{count}',
19
+ hoursZeroTemplate: 'h-0',
20
+ hoursOneTemplate: 'h-1',
21
+ hoursOtherTemplate: 'h-{count}',
22
+ minutesZeroTemplate: 'm-0',
23
+ minutesOneTemplate: 'm-1',
24
+ minutesOtherTemplate: 'm-{count}',
25
+ toNeighborhoodTemplate: 'para {neighborhood}',
26
+ })
27
+
28
+ describe('getDeliveryOptionLabel', () => {
29
+ it.each([
30
+ ['0bd', 'bd-0'],
31
+ ['1bd', 'bd-1'],
32
+ ['5bd', 'bd-5'],
33
+ ['0d', 'd-0'],
34
+ ['1d', 'd-1'],
35
+ ['5d', 'd-5'],
36
+ ['0h', 'h-0'],
37
+ ['1h', 'h-1'],
38
+ ['5h', 'h-5'],
39
+ ['0m', 'm-0'],
40
+ ['1m', 'm-1'],
41
+ ['5m', 'm-5'],
42
+ ])('localizes shipping estimate %s', (shippingEstimate, expected) => {
43
+ expect(
44
+ getDeliveryOptionLabel({ deliveryChannel: '', shippingEstimate }, labels)
45
+ ).toBe(expected)
46
+ })
47
+
48
+ it.each([
49
+ ['delivery', 'Entrega'],
50
+ ['pickup', 'Retirada'],
51
+ ['pickup-in-point', 'Retirada'],
52
+ ['take-away', 'Levar'],
53
+ ])('localizes the %s channel', (deliveryChannel, expected) => {
54
+ expect(
55
+ getDeliveryOptionLabel(
56
+ { deliveryChannel, shippingEstimate: '1d' },
57
+ labels
58
+ )
59
+ ).toBe(`${expected} d-1`)
60
+ })
61
+
62
+ it('localizes the neighborhood connector while preserving its value', () => {
63
+ expect(
64
+ getDeliveryOptionLabel(
65
+ {
66
+ deliveryChannel: 'delivery',
67
+ shippingEstimate: '5bd',
68
+ address: { neighborhood: 'Botafogo' },
69
+ },
70
+ labels
71
+ )
72
+ ).toBe('Entrega bd-5 para Botafogo')
73
+ })
74
+
75
+ it('treats padded one-unit estimates the same way as the API parser', () => {
76
+ expect(
77
+ getDeliveryOptionLabel(
78
+ { deliveryChannel: '', shippingEstimate: '01bd' },
79
+ labels
80
+ )
81
+ ).toBe('bd-1')
82
+ })
83
+
84
+ it('builds a stable key from the same grouping fields as the API', () => {
85
+ expect(
86
+ getDeliveryOptionKey({
87
+ selectedSla: 'Normal',
88
+ deliveryChannel: 'delivery',
89
+ deliveryCompany: 'Correios',
90
+ seller: '1',
91
+ shippingEstimate: '5bd',
92
+ shippingEstimateDate: '2026-08-05T00:00:00Z',
93
+ address: { addressId: 'addr-1' },
94
+ deliveryWindow: {
95
+ startDateUtc: '2026-08-05T10:00:00Z',
96
+ endDateUtc: '2026-08-05T18:00:00Z',
97
+ },
98
+ })
99
+ ).toBe(
100
+ 'Normal|delivery|Correios|1|5bd|2026-08-05T00:00:00Z|addr-1|2026-08-05T10:00:00Z|2026-08-05T18:00:00Z'
101
+ )
102
+ })
103
+
104
+ it('keeps a recognized channel localized when the estimate is unavailable', () => {
105
+ expect(
106
+ getDeliveryOptionLabel(
107
+ {
108
+ deliveryChannel: 'delivery',
109
+ shippingEstimate: 'someday',
110
+ friendlyDeliveryOptionName: 'Delivery someday',
111
+ },
112
+ labels
113
+ )
114
+ ).toBe('Entrega')
115
+ })
116
+
117
+ it('preserves the API fallback for unsupported raw values', () => {
118
+ expect(
119
+ getDeliveryOptionLabel(
120
+ {
121
+ deliveryChannel: 'future-channel',
122
+ shippingEstimate: 'someday',
123
+ friendlyDeliveryOptionName: 'Existing API label',
124
+ },
125
+ labels
126
+ )
127
+ ).toBe('Existing API label')
128
+ })
129
+ })
@@ -0,0 +1,35 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import { getOrderTotalLabel } from '../../../../src/components/account/orders/OrderDetails/SummaryCard/getOrderTotalLabel'
4
+ import { resolveOrderSummaryLabels } from '../../../../src/components/account/orders/OrderDetails/orderDetailsLabels'
5
+
6
+ const labels = resolveOrderSummaryLabels({
7
+ itemsTotalLabel: 'Produtos',
8
+ discountsTotalLabel: 'Descontos',
9
+ shippingTotalLabel: 'Frete',
10
+ taxTotalLabel: 'Impostos',
11
+ changeTotalLabel: 'Alterações',
12
+ })
13
+
14
+ describe('getOrderTotalLabel', () => {
15
+ it.each([
16
+ ['Items', 'Produtos'],
17
+ ['Discounts', 'Descontos'],
18
+ ['Shipping', 'Frete'],
19
+ ['Tax', 'Impostos'],
20
+ ['Change', 'Alterações'],
21
+ ])('localizes the documented OMS %s total', (id, expected) => {
22
+ expect(getOrderTotalLabel({ id, name: `${id} from OMS` }, labels)).toBe(
23
+ expected
24
+ )
25
+ })
26
+
27
+ it('falls back to the OMS name for an unknown total id', () => {
28
+ expect(
29
+ getOrderTotalLabel(
30
+ { id: 'FutureTotal', name: 'Future total from OMS' },
31
+ labels
32
+ )
33
+ ).toBe('Future total from OMS')
34
+ })
35
+ })
@@ -0,0 +1,23 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import {
4
+ defaultListOrdersLabels,
5
+ resolveListOrdersLabels,
6
+ } from '../../../../src/components/account/orders/ListOrders/listOrdersLabels'
7
+
8
+ describe('list orders labels', () => {
9
+ it('provides English defaults for filter group labels', () => {
10
+ expect(defaultListOrdersLabels.statusFilterLabel).toBe('Status')
11
+ expect(defaultListOrdersLabels.orderDateFilterLabel).toBe('Order Date')
12
+ })
13
+
14
+ it('resolves localized filter group labels', () => {
15
+ const labels = resolveListOrdersLabels({
16
+ statusFilterLabel: 'Situação',
17
+ orderDateFilterLabel: 'Data do pedido',
18
+ })
19
+
20
+ expect(labels.statusFilterLabel).toBe('Situação')
21
+ expect(labels.orderDateFilterLabel).toBe('Data do pedido')
22
+ })
23
+ })
@@ -0,0 +1,13 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import {
4
+ defaultOrderDeliveryLabels,
5
+ resolveOrderDeliveryLabels,
6
+ } from '../../../../src/components/account/orders/OrderDetails/orderDetailsLabels'
7
+
8
+ describe('order details labels', () => {
9
+ it('uses English delivery labels by default', () => {
10
+ expect(resolveOrderDeliveryLabels()).toEqual(defaultOrderDeliveryLabels)
11
+ expect(defaultOrderDeliveryLabels.eachLabel).toBe('Each')
12
+ })
13
+ })
@@ -0,0 +1,72 @@
1
+ import '@testing-library/jest-dom/vitest'
2
+ import { cleanup, render, screen } from '@testing-library/react'
3
+ import { afterEach, describe, expect, it, vi } from 'vitest'
4
+
5
+ const mockUseAccountPageData = vi.hoisted(() => vi.fn())
6
+
7
+ vi.mock('next/router', () => ({ useRouter: () => ({ locale: 'pt-BR' }) }))
8
+ vi.mock('src/sdk/account/accountPageContext', () => ({
9
+ useAccountPageData: mockUseAccountPageData,
10
+ }))
11
+
12
+ import AccountOrderDelivery from 'src/components/sections/Account/AccountOrderDelivery'
13
+
14
+ const order = {
15
+ storePreferencesData: { currencyCode: 'BRL' },
16
+ customFields: [],
17
+ deliveryOptionsData: {
18
+ contact: { name: 'Maria' },
19
+ deliveryOptions: [
20
+ {
21
+ selectedSla: 'Normal',
22
+ deliveryChannel: 'delivery',
23
+ shippingEstimate: '2bd',
24
+ friendlyDeliveryOptionName: 'Delivery Up to 2 business days',
25
+ quantityOfDifferentItems: 1,
26
+ total: 1100,
27
+ items: [],
28
+ address: {
29
+ city: 'Rio de Janeiro',
30
+ street: 'Rua Um',
31
+ postalCode: '20000-000',
32
+ state: 'RJ',
33
+ country: 'BRA',
34
+ },
35
+ },
36
+ ],
37
+ },
38
+ }
39
+
40
+ afterEach(() => {
41
+ cleanup()
42
+ mockUseAccountPageData.mockReset()
43
+ })
44
+
45
+ describe('AccountOrderDelivery', () => {
46
+ it('forwards the CMS labels to the delivery card and accordions', () => {
47
+ mockUseAccountPageData.mockReturnValue({ order })
48
+
49
+ render(
50
+ <AccountOrderDelivery
51
+ deliveryTitle="Entrega do pedido"
52
+ deliveryChannelLabel="Entrega"
53
+ businessDaysOtherTemplate="Em até {count} dias úteis"
54
+ recipientLabel="Destinatário"
55
+ />
56
+ )
57
+
58
+ expect(screen.getByText('Entrega do pedido')).toBeInTheDocument()
59
+ expect(screen.getAllByText('Entrega Em até 2 dias úteis')).toHaveLength(2)
60
+ expect(screen.getByText('Destinatário')).toBeInTheDocument()
61
+ })
62
+
63
+ it('renders nothing when the order has no delivery data', () => {
64
+ mockUseAccountPageData.mockReturnValue({
65
+ order: { ...order, deliveryOptionsData: null },
66
+ })
67
+
68
+ const { container } = render(<AccountOrderDelivery />)
69
+
70
+ expect(container).toBeEmptyDOMElement()
71
+ })
72
+ })
@@ -0,0 +1,151 @@
1
+ /**
2
+ * @vitest-environment jsdom
3
+ */
4
+
5
+ import '@testing-library/jest-dom/vitest'
6
+ import { cleanup, render, screen } from '@testing-library/react'
7
+ import { afterEach, describe, expect, it, vi } from 'vitest'
8
+
9
+ const mockUseAccountPageData = vi.hoisted(() => vi.fn())
10
+
11
+ vi.mock('src/sdk/account/accountPageContext', () => ({
12
+ useAccountPageData: mockUseAccountPageData,
13
+ }))
14
+
15
+ vi.mock(
16
+ 'src/components/account/orders/OrderDetails/OrderDetailsHeader',
17
+ () => ({
18
+ OrderDetailsHeader: ({
19
+ labels,
20
+ }: {
21
+ labels: Record<string, string | undefined>
22
+ }) => (
23
+ <div data-testid="order-details-header">
24
+ <span data-testid="reject-modal-title">{labels.rejectModalTitle}</span>
25
+ <span data-testid="reject-modal-message">
26
+ {labels.rejectModalMessage}
27
+ </span>
28
+ <span data-testid="reject-modal-confirm">
29
+ {labels.rejectModalConfirmText}
30
+ </span>
31
+ <span data-testid="approve-success-toast">
32
+ {labels.approveSuccessToast}
33
+ </span>
34
+ <span data-testid="approve-error-toast">
35
+ {labels.approveErrorToast}
36
+ </span>
37
+ <span data-testid="reject-success-toast">
38
+ {labels.rejectSuccessToast}
39
+ </span>
40
+ <span data-testid="reject-error-toast">{labels.rejectErrorToast}</span>
41
+ <span data-testid="pending-further-approvals-alert">
42
+ {labels.pendingFurtherApprovalsAlert}
43
+ </span>
44
+ </div>
45
+ ),
46
+ })
47
+ )
48
+
49
+ import AccountOrderDetails from 'src/components/sections/Account/AccountOrderDetails'
50
+ import { defaultOrderDetailsHeaderLabels } from 'src/components/account/orders/OrderDetails/orderDetailsLabels'
51
+
52
+ const order = { orderId: '123' }
53
+
54
+ afterEach(cleanup)
55
+
56
+ describe('AccountOrderDetails', () => {
57
+ it('forwards the default English reject modal labels when the CMS has no override', () => {
58
+ mockUseAccountPageData.mockReturnValueOnce({ order })
59
+
60
+ render(<AccountOrderDetails />)
61
+
62
+ expect(screen.getByTestId('reject-modal-title')).toHaveTextContent(
63
+ defaultOrderDetailsHeaderLabels.rejectModalTitle
64
+ )
65
+ expect(screen.getByTestId('reject-modal-message').textContent).toBe(
66
+ defaultOrderDetailsHeaderLabels.rejectModalMessage
67
+ )
68
+ expect(screen.getByTestId('reject-modal-confirm')).toHaveTextContent(
69
+ defaultOrderDetailsHeaderLabels.rejectModalConfirmText
70
+ )
71
+ expect(screen.getByTestId('approve-success-toast')).toHaveTextContent(
72
+ defaultOrderDetailsHeaderLabels.approveSuccessToast
73
+ )
74
+ expect(screen.getByTestId('approve-error-toast')).toHaveTextContent(
75
+ defaultOrderDetailsHeaderLabels.approveErrorToast
76
+ )
77
+ expect(screen.getByTestId('reject-success-toast')).toHaveTextContent(
78
+ defaultOrderDetailsHeaderLabels.rejectSuccessToast
79
+ )
80
+ expect(screen.getByTestId('reject-error-toast')).toHaveTextContent(
81
+ defaultOrderDetailsHeaderLabels.rejectErrorToast
82
+ )
83
+ expect(
84
+ screen.getByTestId('pending-further-approvals-alert')
85
+ ).toHaveTextContent(
86
+ defaultOrderDetailsHeaderLabels.pendingFurtherApprovalsAlert
87
+ )
88
+ })
89
+
90
+ it('forwards CMS-provided reject modal labels to the order details header', () => {
91
+ mockUseAccountPageData.mockReturnValueOnce({ order })
92
+
93
+ render(
94
+ <AccountOrderDetails
95
+ rejectModalTitle="Rejeitar solicitação de aprovação"
96
+ rejectModalMessage="Você está rejeitando a política {policy}."
97
+ rejectModalConfirmText="Confirmar rejeição"
98
+ />
99
+ )
100
+
101
+ expect(screen.getByTestId('reject-modal-title')).toHaveTextContent(
102
+ 'Rejeitar solicitação de aprovação'
103
+ )
104
+ expect(screen.getByTestId('reject-modal-message')).toHaveTextContent(
105
+ 'Você está rejeitando a política {policy}.'
106
+ )
107
+ expect(screen.getByTestId('reject-modal-confirm')).toHaveTextContent(
108
+ 'Confirmar rejeição'
109
+ )
110
+ })
111
+
112
+ it('forwards CMS-provided buying policy toast labels to the order details header', () => {
113
+ mockUseAccountPageData.mockReturnValueOnce({ order })
114
+
115
+ render(
116
+ <AccountOrderDetails
117
+ approveSuccessToast="Política {policy} aprovada com sucesso."
118
+ approveErrorToast="Não foi possível aprovar a política."
119
+ rejectSuccessToast="Política {policy} rejeitada. Pedido negado."
120
+ rejectErrorToast="Não foi possível rejeitar a política."
121
+ pendingFurtherApprovalsAlert="Sua aprovação foi registrada. O pedido ainda aguarda outras aprovações."
122
+ />
123
+ )
124
+
125
+ expect(screen.getByTestId('approve-success-toast')).toHaveTextContent(
126
+ 'Política {policy} aprovada com sucesso.'
127
+ )
128
+ expect(screen.getByTestId('approve-error-toast')).toHaveTextContent(
129
+ 'Não foi possível aprovar a política.'
130
+ )
131
+ expect(screen.getByTestId('reject-success-toast')).toHaveTextContent(
132
+ 'Política {policy} rejeitada. Pedido negado.'
133
+ )
134
+ expect(screen.getByTestId('reject-error-toast')).toHaveTextContent(
135
+ 'Não foi possível rejeitar a política.'
136
+ )
137
+ expect(
138
+ screen.getByTestId('pending-further-approvals-alert')
139
+ ).toHaveTextContent(
140
+ 'Sua aprovação foi registrada. O pedido ainda aguarda outras aprovações.'
141
+ )
142
+ })
143
+
144
+ it('renders nothing when the page data has no order', () => {
145
+ mockUseAccountPageData.mockReturnValueOnce({ order: null })
146
+
147
+ const { container } = render(<AccountOrderDetails />)
148
+
149
+ expect(container).toBeEmptyDOMElement()
150
+ })
151
+ })
@@ -0,0 +1,59 @@
1
+ import '@testing-library/jest-dom/vitest'
2
+ import { cleanup, render, screen } from '@testing-library/react'
3
+ import { afterEach, describe, expect, it, vi } from 'vitest'
4
+
5
+ const mockUseAccountPageData = vi.hoisted(() => vi.fn())
6
+
7
+ vi.mock('next/router', () => ({ useRouter: () => ({ locale: 'pt-BR' }) }))
8
+ vi.mock('src/sdk/account/accountPageContext', () => ({
9
+ useAccountPageData: mockUseAccountPageData,
10
+ }))
11
+
12
+ import AccountOrderStatus from 'src/components/sections/Account/AccountOrderStatus'
13
+
14
+ const order = {
15
+ status: 'payment-approved',
16
+ creationDate: '2026-08-05T18:30:00.000Z',
17
+ }
18
+
19
+ afterEach(cleanup)
20
+
21
+ describe('AccountOrderStatus', () => {
22
+ it('renders the timeline with the default labels', () => {
23
+ mockUseAccountPageData.mockReturnValueOnce({ order })
24
+
25
+ render(<AccountOrderStatus />)
26
+
27
+ expect(screen.getByText('Status')).toBeInTheDocument()
28
+ expect(screen.getByText('Order placed')).toBeInTheDocument()
29
+ expect(screen.getByText('Handling order')).toBeInTheDocument()
30
+ })
31
+
32
+ it('forwards the CMS labels to the status card', () => {
33
+ mockUseAccountPageData.mockReturnValueOnce({ order })
34
+
35
+ render(
36
+ <AccountOrderStatus
37
+ statusTitle="Status do pedido"
38
+ orderPlacedStep="Pedido realizado"
39
+ approvedStep="Aprovado"
40
+ handlingStep="Em separação"
41
+ shipOrderStep="Enviar pedido"
42
+ />
43
+ )
44
+
45
+ expect(screen.getByText('Status do pedido')).toBeInTheDocument()
46
+ expect(screen.getByText('Pedido realizado')).toBeInTheDocument()
47
+ expect(screen.getByText('Aprovado')).toBeInTheDocument()
48
+ expect(screen.getByText('Em separação')).toBeInTheDocument()
49
+ expect(screen.getByText('Enviar pedido')).toBeInTheDocument()
50
+ })
51
+
52
+ it('renders nothing when the page data has no order', () => {
53
+ mockUseAccountPageData.mockReturnValueOnce({ order: null })
54
+
55
+ const { container } = render(<AccountOrderStatus />)
56
+
57
+ expect(container).toBeEmptyDOMElement()
58
+ })
59
+ })
@@ -0,0 +1,67 @@
1
+ import '@testing-library/jest-dom/vitest'
2
+ import { cleanup, render } from '@testing-library/react'
3
+ import { afterEach, describe, expect, it, vi } from 'vitest'
4
+
5
+ import type { ListOrdersProps } from 'src/components/account/orders/ListOrders/ListOrders'
6
+
7
+ const mockUseAccountPageData = vi.hoisted(() => vi.fn())
8
+ const mockListOrders = vi.hoisted(() =>
9
+ vi.fn((_props: ListOrdersProps) => null)
10
+ )
11
+
12
+ vi.mock('src/sdk/account/accountPageContext', () => ({
13
+ useAccountPageData: mockUseAccountPageData,
14
+ }))
15
+ vi.mock('src/components/account/orders/ListOrders', () => ({
16
+ ListOrders: mockListOrders,
17
+ }))
18
+
19
+ import AccountOrdersList from 'src/components/sections/Account/AccountOrdersList'
20
+
21
+ const pageData = {
22
+ listOrders: { list: [] },
23
+ total: 0,
24
+ perPage: 10,
25
+ filters: {
26
+ page: 1,
27
+ status: [],
28
+ dateInitial: '',
29
+ dateFinal: '',
30
+ text: '',
31
+ clientEmail: '',
32
+ },
33
+ }
34
+
35
+ afterEach(() => {
36
+ cleanup()
37
+ mockListOrders.mockClear()
38
+ })
39
+
40
+ describe('AccountOrdersList', () => {
41
+ it('forwards the default filter group labels', () => {
42
+ mockUseAccountPageData.mockReturnValueOnce(pageData)
43
+
44
+ render(<AccountOrdersList />)
45
+
46
+ expect(mockListOrders.mock.calls[0][0].labels).toMatchObject({
47
+ statusFilterLabel: 'Status',
48
+ orderDateFilterLabel: 'Order Date',
49
+ })
50
+ })
51
+
52
+ it('forwards the CMS filter group labels', () => {
53
+ mockUseAccountPageData.mockReturnValueOnce(pageData)
54
+
55
+ render(
56
+ <AccountOrdersList
57
+ statusFilterLabel="Situação"
58
+ orderDateFilterLabel="Data do pedido"
59
+ />
60
+ )
61
+
62
+ expect(mockListOrders.mock.calls[0][0].labels).toMatchObject({
63
+ statusFilterLabel: 'Situação',
64
+ orderDateFilterLabel: 'Data do pedido',
65
+ })
66
+ })
67
+ })
@@ -18,12 +18,13 @@ vi.mock('../../../src/server', () => ({
18
18
 
19
19
  const mockedExecute = vi.mocked(execute)
20
20
 
21
- const createResponse = () => {
21
+ const createResponse = ({ headersSent = false } = {}) => {
22
22
  const res = {
23
23
  status: vi.fn().mockReturnThis(),
24
24
  setHeader: vi.fn().mockReturnThis(),
25
25
  send: vi.fn().mockReturnThis(),
26
26
  end: vi.fn().mockReturnThis(),
27
+ headersSent,
27
28
  }
28
29
 
29
30
  return res as unknown as NextApiResponse & typeof res
@@ -122,6 +123,7 @@ describe('/api/graphql error status propagation', () => {
122
123
  expect(res.status).toHaveBeenCalledWith(500)
123
124
  expect(res.end).toHaveBeenCalled()
124
125
  expect(res.send).not.toHaveBeenCalled()
126
+ expect(res.setHeader).toHaveBeenCalledWith('cache-control', 'no-store')
125
127
  })
126
128
 
127
129
  it('exposes type, status and message in the body outside production', async () => {
@@ -168,6 +170,15 @@ describe('/api/graphql error status propagation', () => {
168
170
 
169
171
  expect(res.status).toHaveBeenCalledWith(404)
170
172
  })
173
+
174
+ it('sets cache-control: no-store on error responses so a bare 404/410 is not cached by intermediaries', async () => {
175
+ mockExecuteWithErrors([maskedError(new NotFoundError('missing'))])
176
+
177
+ const res = createResponse()
178
+ await handler(createRequest(), res)
179
+
180
+ expect(res.setHeader).toHaveBeenCalledWith('cache-control', 'no-store')
181
+ })
171
182
  })
172
183
 
173
184
  describe('/api/graphql request handling', () => {
@@ -200,6 +211,21 @@ describe('/api/graphql request handling', () => {
200
211
 
201
212
  expect(res.status).toHaveBeenCalledWith(status)
202
213
  expect(res.end).toHaveBeenCalled()
214
+ expect(res.setHeader).toHaveBeenCalledWith('cache-control', 'no-store')
203
215
  }
204
216
  )
217
+
218
+ it('skips the cache-control header when headers were already sent', async () => {
219
+ mockedExecute.mockRejectedValue(new Error('boom after flush'))
220
+
221
+ const res = createResponse({ headersSent: true })
222
+ await handler(createRequest(), res)
223
+
224
+ expect(res.setHeader).not.toHaveBeenCalledWith(
225
+ 'cache-control',
226
+ expect.anything()
227
+ )
228
+ expect(res.status).toHaveBeenCalledWith(500)
229
+ expect(res.end).toHaveBeenCalled()
230
+ })
205
231
  })