Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
3b4e19fe
Commit
3b4e19fe
authored
Dec 23, 2021
by
Diana Zubova
Committed by
Vitaly Slobodin
Dec 23, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add request to orderPreview for storage purchase
parent
e02f9ce2
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
185 additions
and
41 deletions
+185
-41
ee/app/assets/javascripts/subscriptions/buy_addons_shared/components/app.vue
...cripts/subscriptions/buy_addons_shared/components/app.vue
+0
-3
ee/app/assets/javascripts/subscriptions/buy_addons_shared/components/order_summary.vue
...scriptions/buy_addons_shared/components/order_summary.vue
+47
-9
ee/app/assets/javascripts/subscriptions/buy_addons_shared/components/order_summary/summary_details.vue
...ddons_shared/components/order_summary/summary_details.vue
+13
-8
ee/app/assets/javascripts/subscriptions/buy_addons_shared/constants.js
.../javascripts/subscriptions/buy_addons_shared/constants.js
+1
-0
ee/app/assets/javascripts/subscriptions/graphql/queries/order_preview.customer.query.graphql
...ions/graphql/queries/order_preview.customer.query.graphql
+13
-0
ee/spec/frontend/subscriptions/buy_addons_shared/components/order_summary/summary_details_spec.js
...s_shared/components/order_summary/summary_details_spec.js
+3
-3
ee/spec/frontend/subscriptions/buy_addons_shared/components/order_summary_spec.js
...ptions/buy_addons_shared/components/order_summary_spec.js
+85
-16
ee/spec/frontend/subscriptions/mock_data.js
ee/spec/frontend/subscriptions/mock_data.js
+10
-0
ee/spec/frontend/subscriptions/spec_helper.js
ee/spec/frontend/subscriptions/spec_helper.js
+13
-2
No files found.
ee/app/assets/javascripts/subscriptions/buy_addons_shared/components/app.vue
View file @
3b4e19fe
...
...
@@ -70,9 +70,6 @@ export default {
isAddon
:
true
,
};
},
slotProps
()
{
return
this
.
plan
;
},
totalUnits
()
{
return
this
.
quantity
*
this
.
config
.
quantityPerPack
;
},
...
...
ee/app/assets/javascripts/subscriptions/buy_addons_shared/components/order_summary.vue
View file @
3b4e19fe
<
script
>
import
{
GlIcon
,
GlCollapse
,
GlCollapseToggleDirective
}
from
'
@gitlab/ui
'
;
import
find
from
'
lodash/find
'
;
import
stateQuery
from
'
ee/subscriptions/graphql/queries/state.query.graphql
'
;
import
{
logError
}
from
'
~/lib/logger
'
;
import
{
TAX_RATE
}
from
'
ee/subscriptions/new/constants
'
;
import
{
CUSTOMERSDOT_CLIENT
}
from
'
ee/subscriptions/buy_addons_shared/constants
'
;
import
formattingMixins
from
'
ee/subscriptions/new/formatting_mixins
'
;
import
{
sprintf
}
from
'
~/locale
'
;
import
stateQuery
from
'
ee/subscriptions/graphql/queries/state.query.graphql
'
;
import
orderPreviewQuery
from
'
ee/subscriptions/graphql/queries/order_preview.customer.query.graphql
'
;
import
SummaryDetails
from
'
./order_summary/summary_details.vue
'
;
export
default
{
...
...
@@ -21,6 +26,9 @@ export default {
plan
:
{
type
:
Object
,
required
:
true
,
validator
(
value
)
{
return
Object
.
prototype
.
hasOwnProperty
.
call
(
value
,
'
id
'
);
},
},
title
:
{
type
:
String
,
...
...
@@ -39,6 +47,32 @@ export default {
const
id
=
Number
(
data
.
selectedNamespaceId
);
this
.
selectedNamespace
=
find
(
data
.
eligibleNamespaces
,
{
id
});
this
.
subscription
=
data
.
subscription
;
this
.
selectedNamespaceId
=
data
.
selectedNamespaceId
;
},
},
orderPreview
:
{
client
:
CUSTOMERSDOT_CLIENT
,
query
:
orderPreviewQuery
,
variables
()
{
return
{
namespaceId
:
this
.
selectedNamespaceId
,
newProductId
:
this
.
plan
.
id
,
newProductQuantity
:
this
.
subscription
.
quantity
,
};
},
manual
:
true
,
result
({
data
})
{
if
(
data
.
orderPreview
)
{
this
.
endDate
=
data
.
orderPreview
.
targetDate
;
this
.
proratedAmount
=
data
.
orderPreview
.
amount
;
}
},
error
(
error
)
{
this
.
hasError
=
true
;
logError
(
error
);
},
skip
()
{
return
!
this
.
purchaseHasExpiration
;
},
},
},
...
...
@@ -47,6 +81,9 @@ export default {
isBottomSummaryVisible
:
false
,
selectedNamespace
:
{},
subscription
:
{},
endDate
:
''
,
proratedAmount
:
0
,
hasError
:
false
,
};
},
computed
:
{
...
...
@@ -54,13 +91,15 @@ export default {
return
this
.
plan
.
pricePerYear
;
},
totalExVat
()
{
return
this
.
subscription
.
quantity
*
this
.
selectedPlanPrice
;
return
this
.
isLoading
?
0
:
this
.
proratedAmount
||
this
.
subscription
.
quantity
*
this
.
selectedPlanPrice
;
},
vat
()
{
return
TAX_RATE
*
this
.
totalExVat
;
},
totalAmount
()
{
return
this
.
totalExVat
+
this
.
vat
;
return
this
.
isLoading
?
0
:
this
.
proratedAmount
||
this
.
totalExVat
+
this
.
vat
;
},
quantityPresent
()
{
return
this
.
subscription
.
quantity
>
0
;
...
...
@@ -74,8 +113,8 @@ export default {
titleWithName
()
{
return
sprintf
(
this
.
title
,
{
name
:
this
.
namespaceName
});
},
is
Visible
()
{
return
!
this
.
$apollo
.
loading
;
is
Loading
()
{
return
this
.
$apollo
.
loading
;
},
},
taxRate
:
TAX_RATE
,
...
...
@@ -83,7 +122,6 @@ export default {
</
script
>
<
template
>
<div
v-if=
"isVisible"
class=
"order-summary gl-display-flex gl-flex-direction-column gl-flex-grow-1 gl-mt-2 mt-lg-5"
>
<div
class=
"gl-lg-display-none"
>
...
...
@@ -95,7 +133,7 @@ export default {
<h4
data-testid=
"title"
>
{{
titleWithName
}}
</h4>
</div>
<p
class=
"gl-ml-3"
data-testid=
"amount"
>
{{
formatAmount
(
totalAmount
,
quantityPresent
)
}}
{{
totalAmount
?
formatAmount
(
totalAmount
,
quantityPresent
)
:
'
-
'
}}
</p>
</div>
</div>
...
...
@@ -109,7 +147,7 @@ export default {
:total-amount=
"totalAmount"
:quantity=
"quantity"
:tax-rate=
"$options.taxRate"
:
purchase-has-expiration=
"purchaseHasExpiration
"
:
subscription-end-date=
"endDate
"
>
<template
#price-per-unit
="
{ price }">
<slot
name=
"price-per-unit"
:price=
"price"
></slot>
...
...
@@ -133,7 +171,7 @@ export default {
:total-amount=
"totalAmount"
:quantity=
"quantity"
:tax-rate=
"$options.taxRate"
:
purchase-has-expiration=
"purchaseHasExpiration
"
:
subscription-end-date=
"endDate
"
>
<
template
#price-per-unit=
"{ price }"
>
<slot
name=
"price-per-unit"
:price=
"price"
></slot>
...
...
ee/app/assets/javascripts/subscriptions/buy_addons_shared/components/order_summary/summary_details.vue
View file @
3b4e19fe
...
...
@@ -47,10 +47,10 @@ export default {
required
:
false
,
default
:
null
,
},
purchaseHasExpiration
:
{
type
:
Boolean
,
subscriptionEndDate
:
{
type
:
String
,
required
:
false
,
default
:
false
,
default
:
''
,
},
},
data
()
{
...
...
@@ -60,7 +60,9 @@ export default {
},
computed
:
{
endDate
()
{
return
this
.
startDate
.
setFullYear
(
this
.
startDate
.
getFullYear
()
+
1
);
return
(
this
.
subscriptionEndDate
||
this
.
startDate
.
setFullYear
(
this
.
startDate
.
getFullYear
()
+
1
)
);
},
hasPositiveQuantity
()
{
return
this
.
quantity
>
0
;
...
...
@@ -74,6 +76,9 @@ export default {
formattedPrice
()
{
return
formatNumber
(
this
.
selectedPlanPrice
);
},
renderedAmount
()
{
return
this
.
totalExVat
?
this
.
formatAmount
(
this
.
totalExVat
,
this
.
hasPositiveQuantity
)
:
'
-
'
;
},
},
i18n
:
{
quantity
:
I18N_SUMMARY_QUANTITY
,
...
...
@@ -95,14 +100,14 @@ export default {
}}
<
/span
>
<
/div
>
<
div
>
{{
formatAmount
(
totalExVat
,
hasPositiveQuantity
)
}}
{{
renderedAmount
}}
<
/div
>
<
/div
>
<
div
class
=
"
gl-border-b-1 gl-border-b-gray-100 gl-border-b-solid gl-py-3
"
>
<
div
class
=
"
gl-text-gray-500
"
data
-
testid
=
"
price-per-unit
"
>
<
slot
name
=
"
price-per-unit
"
:
price
=
"
formattedPrice
"
><
/slot
>
<
/div
>
<
div
v
-
if
=
"
purchaseHasExpiration
"
class
=
"
gl-text-gray-500
"
data
-
testid
=
"
subscription-period
"
>
<
div
v
-
if
=
"
subscriptionEndDate
"
class
=
"
gl-text-gray-500
"
data
-
testid
=
"
subscription-period
"
>
{{
sprintf
(
$options
.
i18n
.
dates
,
{
startDate
:
formatDate
(
startDate
),
...
...
@@ -116,7 +121,7 @@ export default {
<
div
class
=
"
gl-display-flex gl-justify-content-space-between gl-text-gray-500
"
>
<
div
>
{{
$options
.
i18n
.
subtotal
}}
<
/div
>
<
div
data
-
testid
=
"
total-ex-vat
"
>
{{
formatAmount
(
totalExVat
,
hasPositiveQuantity
)
}}
{{
renderedAmount
}}
<
/div
>
<
/div
>
<
div
class
=
"
gl-display-flex gl-justify-content-space-between gl-text-gray-500
"
>
...
...
@@ -141,7 +146,7 @@ export default {
>
<
div
>
{{
$options
.
i18n
.
total
}}
<
/div
>
<
div
data
-
testid
=
"
total-amount
"
>
{{
formatAmount
(
totalAmount
,
hasPositiveQuantity
)
}}
{{
renderedAmount
}}
<
/div
>
<
/div
>
<
/div
>
...
...
ee/app/assets/javascripts/subscriptions/buy_addons_shared/constants.js
View file @
3b4e19fe
...
...
@@ -12,6 +12,7 @@ export const CUSTOMER_TYPE = 'Customer';
export
const
SUBSCRIPTION_TYPE
=
'
Subscription
'
;
export
const
NAMESPACE_TYPE
=
'
Namespace
'
;
export
const
PAYMENT_METHOD_TYPE
=
'
PaymentMethod
'
;
export
const
ORDER_PREVIEW_TYPE
=
'
OrderPreview
'
;
export
const
PLAN_TYPE
=
'
Plan
'
;
export
const
STEP_TYPE
=
'
Step
'
;
export
const
COUNTRY_TYPE
=
'
Country
'
;
...
...
ee/app/assets/javascripts/subscriptions/graphql/queries/order_preview.customer.query.graphql
0 → 100644
View file @
3b4e19fe
query
orderPreview
(
$namespaceId
:
ID
!,
$newProductId
:
String
!,
$newProductQuantity
:
Int
!)
{
orderPreview
(
namespaceId
:
$namespaceId
newProductId
:
$newProductId
newProductQuantity
:
$newProductQuantity
)
{
targetDate
amount
amountWithoutTax
prorationCredit
priceBeforeProration
}
}
ee/spec/frontend/subscriptions/buy_addons_shared/components/order_summary/summary_details_spec.js
View file @
3b4e19fe
...
...
@@ -83,18 +83,18 @@ describe('SummaryDetails', () => {
describe
(
'
when subscription has expiration
'
,
()
=>
{
beforeEach
(()
=>
{
wrapper
=
createComponent
({
purchaseHasExpiration
:
true
});
wrapper
=
createComponent
({
subscriptionEndDate
:
'
2021-02-06
'
});
});
it
(
'
renders subscription period
'
,
()
=>
{
expect
(
findSubscriptionPeriod
().
isVisible
()).
toBe
(
true
);
expect
(
findSubscriptionPeriod
().
text
()).
toBe
(
'
Jul 6, 2020 -
Jul
6, 2021
'
);
expect
(
findSubscriptionPeriod
().
text
()).
toBe
(
'
Jul 6, 2020 -
Feb
6, 2021
'
);
});
});
describe
(
'
when subscription does not have expiration
'
,
()
=>
{
beforeEach
(()
=>
{
wrapper
=
createComponent
({
purchaseHasExpiration
:
false
});
wrapper
=
createComponent
({
subscriptionEndDate
:
''
});
});
it
(
'
does not render subscription period
'
,
()
=>
{
...
...
ee/spec/frontend/subscriptions/buy_addons_shared/components/order_summary_spec.js
View file @
3b4e19fe
...
...
@@ -7,11 +7,15 @@ import subscriptionsResolvers from 'ee/subscriptions/buy_addons_shared/graphql/r
import
stateQuery
from
'
ee/subscriptions/graphql/queries/state.query.graphql
'
;
import
purchaseFlowResolvers
from
'
ee/vue_shared/purchase_flow/graphql/resolvers
'
;
import
{
mock
CiMinutes
Plans
,
mock
Storage
Plans
,
mockParsedNamespaces
,
mockOrderPreview
,
stateData
as
mockStateData
,
}
from
'
ee_jest/subscriptions/mock_data
'
;
import
createMockApollo
from
'
helpers/mock_apollo_helper
'
;
import
createMockApollo
,
{
createMockClient
}
from
'
helpers/mock_apollo_helper
'
;
import
orderPreviewQuery
from
'
ee/subscriptions/graphql/queries/order_preview.customer.query.graphql
'
;
import
{
CUSTOMERSDOT_CLIENT
}
from
'
ee/subscriptions/buy_addons_shared/constants
'
;
const
localVue
=
createLocalVue
();
localVue
.
use
(
VueApollo
);
...
...
@@ -29,24 +33,25 @@ describe('Order Summary', () => {
const
findAmount
=
()
=>
wrapper
.
findByTestId
(
'
amount
'
);
const
findTitle
=
()
=>
wrapper
.
findByTestId
(
'
title
'
);
const
createMockApolloProvider
=
(
stateData
=
{})
=>
{
const
createMockApolloProvider
=
(
stateData
=
{}
,
mockRequest
=
{}
)
=>
{
const
mockApollo
=
createMockApollo
([],
resolvers
);
const
data
=
merge
({},
mockStateData
,
initialStateData
,
stateData
);
mockApollo
.
clients
.
defaultClient
.
cache
.
writeQuery
({
query
:
stateQuery
,
data
,
});
mockApollo
.
clients
[
CUSTOMERSDOT_CLIENT
]
=
createMockClient
([[
orderPreviewQuery
,
mockRequest
]]);
return
mockApollo
;
};
const
createComponent
=
(
stateData
)
=>
{
const
apolloProvider
=
createMockApolloProvider
(
stateData
);
const
createComponent
=
(
apolloProvider
,
props
)
=>
{
wrapper
=
shallowMountExtended
(
OrderSummary
,
{
localVue
,
apolloProvider
,
propsData
:
{
plan
:
mockCiMinutesPlans
[
0
],
title
:
"
%{name}'s CI minutes
"
,
plan
:
mockStoragePlans
[
0
],
title
:
"
%{name}'s storage subscription
"
,
...
props
,
},
});
};
...
...
@@ -57,37 +62,101 @@ describe('Order Summary', () => {
describe
(
'
the default plan
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
({
subscription
:
{
quantity
:
1
},
});
const
apolloProvider
=
createMockApolloProvider
({
subscription
:
{
quantity
:
1
}
});
createComponent
(
apolloProvider
);
});
it
(
'
displays the title
'
,
()
=>
{
expect
(
findTitle
().
text
()).
toMatchInterpolatedText
(
"
Gitlab Org's
CI minutes
"
);
expect
(
findTitle
().
text
()).
toMatchInterpolatedText
(
"
Gitlab Org's
storage subscription
"
);
});
});
describe
(
'
when quantity is greater than zero
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
({
subscription
:
{
quantity
:
3
},
});
const
apolloProvider
=
createMockApolloProvider
({
subscription
:
{
quantity
:
3
}
});
createComponent
(
apolloProvider
);
});
it
(
'
renders amount
'
,
()
=>
{
expect
(
findAmount
().
text
()).
toBe
(
'
$
3
0
'
);
expect
(
findAmount
().
text
()).
toBe
(
'
$
18
0
'
);
});
});
describe
(
'
when quantity is less than or equal to zero
'
,
()
=>
{
beforeEach
(()
=>
{
c
reateComponent
({
c
onst
apolloProvider
=
createMockApolloProvider
({
subscription
:
{
quantity
:
0
},
});
createComponent
(
apolloProvider
);
});
it
(
'
does not render amount
'
,
()
=>
{
expect
(
findAmount
().
text
()).
toBe
(
'
-
'
);
});
});
describe
(
'
when subscription has expiration date
'
,
()
=>
{
describe
(
'
calls api that returns prorated amount
'
,
()
=>
{
beforeEach
(()
=>
{
const
orderPreviewQueryMock
=
jest
.
fn
()
.
mockResolvedValue
({
data
:
{
orderPreview
:
mockOrderPreview
}
});
const
apolloProvider
=
createMockApolloProvider
(
{
subscription
:
{
quantity
:
1
}
},
orderPreviewQueryMock
,
);
createComponent
(
apolloProvider
,
{
purchaseHasExpiration
:
true
});
});
it
(
'
renders prorated amount
'
,
()
=>
{
expect
(
findAmount
().
text
()).
toBe
(
'
$59.67
'
);
});
});
describe
(
'
calls api that returns empty value
'
,
()
=>
{
beforeEach
(()
=>
{
const
orderPreviewQueryMock
=
jest
.
fn
().
mockResolvedValue
({
data
:
{
orderPreview
:
null
}
});
const
apolloProvider
=
createMockApolloProvider
(
{
subscription
:
{
quantity
:
1
}
},
orderPreviewQueryMock
,
);
createComponent
(
apolloProvider
,
{
purchaseHasExpiration
:
true
});
});
it
(
'
renders amount from the state
'
,
()
=>
{
expect
(
findAmount
().
text
()).
toBe
(
'
$60
'
);
});
});
describe
(
'
calls api that returns no data
'
,
()
=>
{
beforeEach
(()
=>
{
jest
.
spyOn
(
console
,
'
error
'
).
mockImplementation
(()
=>
{});
const
orderPreviewQueryMock
=
jest
.
fn
().
mockResolvedValue
({
data
:
null
});
const
apolloProvider
=
createMockApolloProvider
(
{
subscription
:
{
quantity
:
1
}
},
orderPreviewQueryMock
,
);
createComponent
(
apolloProvider
,
{
purchaseHasExpiration
:
true
});
});
it
(
'
renders amount from the state
'
,
()
=>
{
expect
(
findAmount
().
text
()).
toBe
(
'
$60
'
);
});
});
describe
(
'
when api is loading
'
,
()
=>
{
beforeEach
(()
=>
{
const
orderPreviewQueryMock
=
jest
.
fn
().
mockResolvedValue
(
new
Promise
(()
=>
{}));
const
apolloProvider
=
createMockApolloProvider
(
{
subscription
:
{
quantity
:
1
}
},
orderPreviewQueryMock
,
);
createComponent
(
apolloProvider
,
{
purchaseHasExpiration
:
true
});
});
it
(
'
does not render amount when api is loading
'
,
()
=>
{
expect
(
findAmount
().
text
()).
toBe
(
'
-
'
);
});
});
});
});
ee/spec/frontend/subscriptions/mock_data.js
View file @
3b4e19fe
...
...
@@ -5,6 +5,7 @@ import {
PAYMENT_METHOD_TYPE
,
PLAN_TYPE
,
SUBSCRIPTION_TYPE
,
ORDER_PREVIEW_TYPE
,
}
from
'
ee/subscriptions/buy_addons_shared/constants
'
;
export
const
accountId
=
'
111111111111
'
;
...
...
@@ -50,6 +51,15 @@ export const mockDefaultCache = {
redirectAfterSuccess
:
'
/
'
,
};
export
const
mockOrderPreview
=
{
targetDate
:
'
2022-12-15
'
,
amount
:
59.67
,
amountWithoutTax
:
60.0
,
prorationCredit
:
0.33
,
priceBeforeProration
:
60.0
,
__typename
:
ORDER_PREVIEW_TYPE
,
};
export
const
stateData
=
{
eligibleNamespaces
:
[],
subscription
:
{
...
...
ee/spec/frontend/subscriptions/spec_helper.js
View file @
3b4e19fe
import
VueApollo
from
'
vue-apollo
'
;
import
{
writeInitialDataToApolloCache
}
from
'
ee/subscriptions/buy_addons_shared/utils
'
;
import
plansQuery
from
'
ee/subscriptions/graphql/queries/plans.customer.query.graphql
'
;
import
orderPreviewQuery
from
'
ee/subscriptions/graphql/queries/order_preview.customer.query.graphql
'
;
import
{
createMockClient
}
from
'
helpers/mock_apollo_helper
'
;
import
{
CUSTOMERSDOT_CLIENT
}
from
'
ee/subscriptions/buy_addons_shared/constants
'
;
import
{
mockCiMinutesPlans
,
mockDefaultCache
}
from
'
ee_jest/subscriptions/mock_data
'
;
import
{
mockCiMinutesPlans
,
mockDefaultCache
,
mockOrderPreview
,
}
from
'
ee_jest/subscriptions/mock_data
'
;
export
function
createMockApolloProvider
(
mockResponses
=
{},
dataset
=
{})
{
const
{
plansQueryMock
=
jest
.
fn
().
mockResolvedValue
({
data
:
{
plans
:
mockCiMinutesPlans
}
}),
orderPreviewQueryMock
=
jest
.
fn
()
.
mockResolvedValue
({
data
:
{
orderPreview
:
mockOrderPreview
}
}),
}
=
mockResponses
;
const
{
quantity
}
=
dataset
;
const
mockDefaultClient
=
createMockClient
();
const
mockCustomersDotClient
=
createMockClient
([[
plansQuery
,
plansQueryMock
]]);
const
mockCustomersDotClient
=
createMockClient
([
[
plansQuery
,
plansQueryMock
],
[
orderPreviewQuery
,
orderPreviewQueryMock
],
]);
const
apolloProvider
=
new
VueApollo
({
defaultClient
:
mockDefaultClient
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment