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
1d1836af
Commit
1d1836af
authored
Dec 10, 2019
by
Alex Buijs
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add subscription details component for paid signup
This is part of the paid signup flow MR
parent
297cf2c9
Changes
20
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
665 additions
and
80 deletions
+665
-80
ee/app/assets/javascripts/subscriptions/new/components/checkout.vue
...ets/javascripts/subscriptions/new/components/checkout.vue
+3
-1
ee/app/assets/javascripts/subscriptions/new/components/checkout/components/step.vue
...subscriptions/new/components/checkout/components/step.vue
+4
-4
ee/app/assets/javascripts/subscriptions/new/components/checkout/components/step_summary.vue
...tions/new/components/checkout/components/step_summary.vue
+2
-2
ee/app/assets/javascripts/subscriptions/new/components/checkout/subscription_details.vue
...riptions/new/components/checkout/subscription_details.vue
+157
-0
ee/app/assets/javascripts/subscriptions/new/constants.js
ee/app/assets/javascripts/subscriptions/new/constants.js
+1
-2
ee/app/assets/javascripts/subscriptions/new/index.js
ee/app/assets/javascripts/subscriptions/new/index.js
+3
-6
ee/app/assets/javascripts/subscriptions/new/store/actions.js
ee/app/assets/javascripts/subscriptions/new/store/actions.js
+23
-7
ee/app/assets/javascripts/subscriptions/new/store/getters.js
ee/app/assets/javascripts/subscriptions/new/store/getters.js
+6
-1
ee/app/assets/javascripts/subscriptions/new/store/index.js
ee/app/assets/javascripts/subscriptions/new/store/index.js
+3
-5
ee/app/assets/javascripts/subscriptions/new/store/mutation_types.js
...ets/javascripts/subscriptions/new/store/mutation_types.js
+8
-2
ee/app/assets/javascripts/subscriptions/new/store/mutations.js
...p/assets/javascripts/subscriptions/new/store/mutations.js
+18
-2
ee/app/assets/javascripts/subscriptions/new/store/state.js
ee/app/assets/javascripts/subscriptions/new/store/state.js
+29
-3
ee/app/assets/stylesheets/pages/subscriptions.scss
ee/app/assets/stylesheets/pages/subscriptions.scss
+1
-1
ee/spec/frontend/subscriptions/new/components/checkout/components/step_spec.js
...criptions/new/components/checkout/components/step_spec.js
+24
-35
ee/spec/frontend/subscriptions/new/components/checkout/subscription_details_spec.js
...ions/new/components/checkout/subscription_details_spec.js
+125
-0
ee/spec/frontend/subscriptions/new/store/actions_spec.js
ee/spec/frontend/subscriptions/new/store/actions_spec.js
+67
-4
ee/spec/frontend/subscriptions/new/store/getters_spec.js
ee/spec/frontend/subscriptions/new/store/getters_spec.js
+28
-3
ee/spec/frontend/subscriptions/new/store/mutations_spec.js
ee/spec/frontend/subscriptions/new/store/mutations_spec.js
+38
-2
ee/spec/frontend/subscriptions/new/store/state_spec.js
ee/spec/frontend/subscriptions/new/store/state_spec.js
+95
-0
locale/gitlab.pot
locale/gitlab.pot
+30
-0
No files found.
ee/app/assets/javascripts/subscriptions/new/components/checkout.vue
View file @
1d1836af
<
script
>
import
{
s__
}
from
'
~/locale
'
;
import
ProgressBar
from
'
./checkout/progress_bar.vue
'
;
import
SubscriptionDetails
from
'
./checkout/subscription_details.vue
'
;
export
default
{
components
:
{
ProgressBar
},
components
:
{
ProgressBar
,
SubscriptionDetails
},
i18n
:
{
checkout
:
s__
(
'
Checkout|Checkout
'
),
},
...
...
@@ -15,6 +16,7 @@ export default {
<progress-bar
:step=
"2"
/>
<div
class=
"flash-container"
></div>
<h2
class=
"mt-4 mb-3 mb-lg-5"
>
{{
$options
.
i18n
.
checkout
}}
</h2>
<subscription-details
/>
</div>
</div>
</
template
>
ee/app/assets/javascripts/subscriptions/new/components/checkout/components/step.vue
View file @
1d1836af
...
...
@@ -37,10 +37,10 @@ export default {
isFinished
()
{
return
this
.
isValid
&&
!
this
.
isActive
;
},
e
ditable
()
{
return
this
.
isFinished
&&
this
.
stepIndex
(
this
.
step
)
<
this
.
active
StepIndex
;
isE
ditable
()
{
return
this
.
isFinished
&&
this
.
stepIndex
(
this
.
step
)
<
this
.
current
StepIndex
;
},
...
mapGetters
([
'
currentStep
'
,
'
stepIndex
'
,
'
active
StepIndex
'
]),
...
mapGetters
([
'
currentStep
'
,
'
stepIndex
'
,
'
current
StepIndex
'
]),
},
methods
:
{
...
mapActions
([
'
activateStep
'
,
'
activateNextStep
'
]),
...
...
@@ -67,7 +67,7 @@ export default {
</gl-button>
</gl-form-group>
</div>
<step-summary
v-if=
"isFinished"
:
editable=
"e
ditable"
:edit=
"edit"
>
<step-summary
v-if=
"isFinished"
:
is-editable=
"isE
ditable"
:edit=
"edit"
>
<slot
name=
"summary"
></slot>
</step-summary>
</div>
...
...
ee/app/assets/javascripts/subscriptions/new/components/checkout/components/step_summary.vue
View file @
1d1836af
...
...
@@ -7,7 +7,7 @@ export default {
GlButton
,
},
props
:
{
e
ditable
:
{
isE
ditable
:
{
type
:
Boolean
,
required
:
true
,
},
...
...
@@ -26,7 +26,7 @@ export default {
<div>
<slot></slot>
</div>
<div
v-if=
"
e
ditable"
class=
"d-flex flex-column justify-content-center"
>
<div
v-if=
"
isE
ditable"
class=
"d-flex flex-column justify-content-center"
>
<gl-button
@
click=
"edit"
>
{{
$options
.
i18n
.
edit
}}
</gl-button>
</div>
</div>
...
...
ee/app/assets/javascripts/subscriptions/new/components/checkout/subscription_details.vue
0 → 100644
View file @
1d1836af
<
script
>
import
_
from
'
underscore
'
;
import
autofocusonshow
from
'
~/vue_shared/directives/autofocusonshow
'
;
import
{
mapState
,
mapGetters
,
mapActions
}
from
'
vuex
'
;
import
{
GlFormGroup
,
GlFormSelect
,
GlFormInput
,
GlSprintf
,
GlLink
}
from
'
@gitlab/ui
'
;
import
{
sprintf
,
s__
}
from
'
~/locale
'
;
import
Step
from
'
./components/step.vue
'
;
export
default
{
components
:
{
GlFormGroup
,
GlFormSelect
,
GlFormInput
,
GlSprintf
,
GlLink
,
Step
,
},
directives
:
{
autofocusonshow
,
},
computed
:
{
...
mapState
([
'
availablePlans
'
,
'
selectedPlan
'
,
'
isSetupForCompany
'
,
'
organizationName
'
,
'
numberOfUsers
'
,
]),
...
mapGetters
([
'
selectedPlanText
'
]),
selectedPlanModel
:
{
get
()
{
return
this
.
selectedPlan
;
},
set
(
selectedPlan
)
{
this
.
updateSelectedPlan
(
selectedPlan
);
},
},
numberOfUsersModel
:
{
get
()
{
return
this
.
numberOfUsers
;
},
set
(
number
)
{
this
.
updateNumberOfUsers
(
number
);
},
},
organizationNameModel
:
{
get
()
{
return
this
.
organizationName
;
},
set
(
organizationName
)
{
this
.
updateOrganizationName
(
organizationName
);
},
},
selectedPlanTextLine
()
{
return
sprintf
(
this
.
$options
.
i18n
.
selectedPlan
,
{
selectedPlanText
:
this
.
selectedPlanText
});
},
isValid
()
{
if
(
this
.
isSetupForCompany
)
{
return
(
!
_
.
isEmpty
(
this
.
selectedPlan
)
&&
!
_
.
isEmpty
(
this
.
organizationName
)
&&
this
.
numberOfUsers
>
0
);
}
return
!
_
.
isEmpty
(
this
.
selectedPlan
)
&&
this
.
numberOfUsers
===
1
;
},
},
methods
:
{
...
mapActions
([
'
updateSelectedPlan
'
,
'
toggleIsSetupForCompany
'
,
'
updateNumberOfUsers
'
,
'
updateOrganizationName
'
,
]),
},
i18n
:
{
stepTitle
:
s__
(
'
Checkout|Subscription details
'
),
nextStepButtonText
:
s__
(
'
Checkout|Continue to billing
'
),
selectedPlanLabel
:
s__
(
'
Checkout|GitLab plan
'
),
organizationNameLabel
:
s__
(
'
Checkout|Name of company or organization using GitLab
'
),
numberOfUsersLabel
:
s__
(
'
Checkout|Number of users
'
),
needMoreUsersLink
:
s__
(
'
Checkout|Need more users? Purchase GitLab for your %{company}.
'
),
companyOrTeam
:
s__
(
'
Checkout|company or team
'
),
selectedPlan
:
s__
(
'
Checkout|%{selectedPlanText} plan
'
),
group
:
s__
(
'
Checkout|Group
'
),
users
:
s__
(
'
Checkout|Users
'
),
},
};
</
script
>
<
template
>
<step
step=
"subscriptionDetails"
:title=
"$options.i18n.stepTitle"
:is-valid=
"isValid"
:next-step-button-text=
"$options.i18n.nextStepButtonText"
>
<template
#body
>
<gl-form-group
:label=
"$options.i18n.selectedPlanLabel"
label-size=
"sm"
label-for=
"selectedPlan"
class=
"append-bottom-default"
>
<gl-form-select
id=
"selectedPlan"
v-model=
"selectedPlanModel"
v-autofocusonshow
:options=
"availablePlans"
/>
</gl-form-group>
<gl-form-group
v-if=
"isSetupForCompany"
:label=
"$options.i18n.organizationNameLabel"
label-size=
"sm"
label-for=
"organizationName"
class=
"append-bottom-default"
>
<gl-form-input
id=
"organizationName"
v-model=
"organizationNameModel"
type=
"text"
/>
</gl-form-group>
<div
class=
"combined d-flex"
>
<gl-form-group
:label=
"$options.i18n.numberOfUsersLabel"
label-size=
"sm"
label-for=
"numberOfUsers"
class=
"number"
>
<gl-form-input
id=
"numberOfUsers"
v-model.number=
"numberOfUsersModel"
type=
"number"
min=
"0"
:disabled=
"!isSetupForCompany"
/>
</gl-form-group>
<gl-form-group
v-if=
"!isSetupForCompany"
class=
"label prepend-left-default align-self-end company-link"
>
<gl-sprintf
:message=
"$options.i18n.needMoreUsersLink"
>
<template
#company
>
<gl-link
@
click=
"toggleIsSetupForCompany"
>
{{
$options
.
i18n
.
companyOrTeam
}}
</gl-link>
</
template
>
</gl-sprintf>
</gl-form-group>
</div>
</template>
<
template
#summary
>
<strong
class=
"js-summary-line-1"
>
{{
selectedPlanTextLine
}}
</strong>
<div
v-if=
"isSetupForCompany"
class=
"js-summary-line-2"
>
{{
$options
.
i18n
.
group
}}
:
{{
organizationName
}}
</div>
<div
class=
"js-summary-line-3"
>
{{
$options
.
i18n
.
users
}}
:
{{
numberOfUsers
}}
</div>
</
template
>
</step>
</template>
ee/app/assets/javascripts/subscriptions/new/constants.js
View file @
1d1836af
/* eslint-disable import/prefer-default-export */
export
const
STEPS
=
[];
export
const
STEPS
=
[
'
subscriptionDetails
'
];
ee/app/assets/javascripts/subscriptions/new/index.js
View file @
1d1836af
import
Vue
from
'
vue
'
;
import
s
tore
from
'
./store
'
;
import
createS
tore
from
'
./store
'
;
import
Checkout
from
'
./components/checkout.vue
'
;
export
default
()
=>
{
...
...
@@ -7,12 +7,9 @@ export default () => {
return
new
Vue
({
el
:
checkoutEl
,
store
,
components
:
{
Checkout
,
},
store
:
createStore
(
checkoutEl
.
dataset
),
render
(
createElement
)
{
return
createElement
(
'
checkout
'
,
{}
);
return
createElement
(
Checkout
);
},
});
};
ee/app/assets/javascripts/subscriptions/new/store/actions.js
View file @
1d1836af
import
*
as
types
from
'
./mutation_types
'
;
import
{
STEPS
}
from
'
../constants
'
;
export
const
activateStep
=
({
commit
},
s
tep
)
=>
{
if
(
STEPS
.
includes
(
s
tep
))
{
commit
(
types
.
ACTIVATE_STEP
,
s
tep
);
export
const
activateStep
=
({
commit
},
currentS
tep
)
=>
{
if
(
STEPS
.
includes
(
currentS
tep
))
{
commit
(
types
.
UPDATE_CURRENT_STEP
,
currentS
tep
);
}
};
export
const
activateNextStep
=
({
commit
,
getters
})
=>
{
const
{
active
StepIndex
}
=
getters
;
const
{
current
StepIndex
}
=
getters
;
if
(
active
StepIndex
<
STEPS
.
length
-
1
)
{
const
nextStep
=
STEPS
[
active
StepIndex
+
1
];
if
(
current
StepIndex
<
STEPS
.
length
-
1
)
{
const
nextStep
=
STEPS
[
current
StepIndex
+
1
];
commit
(
types
.
ACTIVATE
_STEP
,
nextStep
);
commit
(
types
.
UPDATE_CURRENT
_STEP
,
nextStep
);
}
};
export
const
updateSelectedPlan
=
({
commit
},
selectedPlan
)
=>
{
commit
(
types
.
UPDATE_SELECTED_PLAN
,
selectedPlan
);
};
export
const
toggleIsSetupForCompany
=
({
state
,
commit
})
=>
{
commit
(
types
.
UPDATE_IS_SETUP_FOR_COMPANY
,
!
state
.
isSetupForCompany
);
};
export
const
updateNumberOfUsers
=
({
commit
},
numberOfUsers
)
=>
{
commit
(
types
.
UPDATE_NUMBER_OF_USERS
,
numberOfUsers
||
0
);
};
export
const
updateOrganizationName
=
({
commit
},
organizationName
)
=>
{
commit
(
types
.
UPDATE_ORGANIZATION_NAME
,
organizationName
);
};
ee/app/assets/javascripts/subscriptions/new/store/getters.js
View file @
1d1836af
...
...
@@ -4,4 +4,9 @@ export const currentStep = state => state.currentStep;
export
const
stepIndex
=
()
=>
step
=>
STEPS
.
findIndex
(
el
=>
el
===
step
);
export
const
activeStepIndex
=
(
state
,
getters
)
=>
getters
.
stepIndex
(
state
.
currentStep
);
export
const
currentStepIndex
=
(
state
,
getters
)
=>
getters
.
stepIndex
(
state
.
currentStep
);
export
const
selectedPlanText
=
(
state
,
getters
)
=>
getters
.
selectedPlanDetails
.
text
;
export
const
selectedPlanDetails
=
state
=>
state
.
availablePlans
.
find
(
plan
=>
plan
.
value
===
state
.
selectedPlan
);
ee/app/assets/javascripts/subscriptions/new/store/index.js
View file @
1d1836af
...
...
@@ -4,16 +4,14 @@ import Vuex from 'vuex';
import
*
as
actions
from
'
./actions
'
;
import
*
as
getters
from
'
./getters
'
;
import
mutations
from
'
./mutations
'
;
import
s
tate
from
'
./state
'
;
import
createS
tate
from
'
./state
'
;
Vue
.
use
(
Vuex
);
const
createStore
=
(
)
=>
export
default
(
initialState
=
{}
)
=>
new
Vuex
.
Store
({
state
:
createState
(
initialState
),
actions
,
getters
,
mutations
,
state
,
});
export
default
createStore
();
ee/app/assets/javascripts/subscriptions/new/store/mutation_types.js
View file @
1d1836af
/* eslint-disable import/prefer-default-export */
export
const
UPDATE_CURRENT_STEP
=
'
UPDATE_CURRENT_STEP
'
;
export
const
ACTIVATE_STEP
=
'
ACTIVATE_STEP
'
;
export
const
UPDATE_SELECTED_PLAN
=
'
UPDATE_SELECTED_PLAN
'
;
export
const
UPDATE_IS_SETUP_FOR_COMPANY
=
'
UPDATE_IS_SETUP_FOR_COMPANY
'
;
export
const
UPDATE_NUMBER_OF_USERS
=
'
UPDATE_NUMBER_OF_USERS
'
;
export
const
UPDATE_ORGANIZATION_NAME
=
'
UPDATE_ORGANIZATION_NAME
'
;
ee/app/assets/javascripts/subscriptions/new/store/mutations.js
View file @
1d1836af
import
*
as
types
from
'
./mutation_types
'
;
export
default
{
[
types
.
ACTIVATE_STEP
](
state
,
step
)
{
state
.
currentStep
=
step
;
[
types
.
UPDATE_CURRENT_STEP
](
state
,
currentStep
)
{
state
.
currentStep
=
currentStep
;
},
[
types
.
UPDATE_SELECTED_PLAN
](
state
,
selectedPlan
)
{
state
.
selectedPlan
=
selectedPlan
;
},
[
types
.
UPDATE_IS_SETUP_FOR_COMPANY
](
state
,
isSetupForCompany
)
{
state
.
isSetupForCompany
=
isSetupForCompany
;
},
[
types
.
UPDATE_NUMBER_OF_USERS
](
state
,
numberOfUsers
)
{
state
.
numberOfUsers
=
numberOfUsers
;
},
[
types
.
UPDATE_ORGANIZATION_NAME
](
state
,
organizationName
)
{
state
.
organizationName
=
organizationName
;
},
};
ee/app/assets/javascripts/subscriptions/new/store/state.js
View file @
1d1836af
import
{
capitalizeFirstCharacter
}
from
'
~/lib/utils/text_utility
'
;
import
{
parseBoolean
}
from
'
~/lib/utils/common_utils
'
;
import
{
STEPS
}
from
'
../constants
'
;
export
default
()
=>
({
currentStep
:
STEPS
[
0
],
});
const
parsePlanData
=
planData
=>
JSON
.
parse
(
planData
).
map
(
plan
=>
({
value
:
plan
.
id
,
text
:
capitalizeFirstCharacter
(
plan
.
code
),
pricePerUserPerYear
:
plan
.
price_per_year
,
}));
const
determineSelectedPlan
=
(
planId
,
plans
)
=>
{
if
(
planId
&&
plans
.
find
(
plan
=>
plan
.
value
===
planId
))
{
return
planId
;
}
return
plans
[
0
]
&&
plans
[
0
].
value
;
};
export
default
({
planData
=
'
[]
'
,
planId
,
setupForCompany
,
fullName
})
=>
{
const
plans
=
parsePlanData
(
planData
);
return
{
currentStep
:
STEPS
[
0
],
availablePlans
:
plans
,
selectedPlan
:
determineSelectedPlan
(
planId
,
plans
),
isSetupForCompany
:
parseBoolean
(
setupForCompany
),
fullName
,
organizationName
:
null
,
numberOfUsers
:
parseBoolean
(
setupForCompany
)
?
0
:
1
,
};
};
ee/app/assets/stylesheets/pages/subscriptions.scss
View file @
1d1836af
...
...
@@ -24,7 +24,7 @@
@media
(
min-width
:
map-get
(
$grid-breakpoints
,
lg
))
{
justify-content
:
inherit
!
important
;
margin-top
:
36px
;
margin-top
:
$gl-padding-32
;
max-width
:
none
;
}
...
...
ee/spec/frontend/subscriptions/new/components/checkout/components/step_spec.js
View file @
1d1836af
import
Vuex
from
'
vuex
'
;
import
{
shallowMount
,
createLocalVue
}
from
'
@vue/test-utils
'
;
import
{
GlButton
}
from
'
@gitlab/ui
'
;
import
s
tore
from
'
ee/subscriptions/new/store
'
;
import
createS
tore
from
'
ee/subscriptions/new/store
'
;
import
*
as
constants
from
'
ee/subscriptions/new/constants
'
;
import
component
from
'
ee/subscriptions/new/components/checkout/components/step.vue
'
;
import
StepSummary
from
'
ee/subscriptions/new/components/checkout/components/step_summary.vue
'
;
...
...
@@ -12,7 +12,9 @@ describe('Step', () => {
let
wrapper
;
const
initialData
=
{
const
store
=
createStore
();
const
initialProps
=
{
step
:
'
secondStep
'
,
isValid
:
true
,
title
:
'
title
'
,
...
...
@@ -22,7 +24,7 @@ describe('Step', () => {
const
factory
=
propsData
=>
{
wrapper
=
shallowMount
(
localVue
.
extend
(
component
),
{
store
,
propsData
:
{
...
initial
Data
,
...
propsData
},
propsData
:
{
...
initial
Props
,
...
propsData
},
localVue
,
sync
:
false
,
});
...
...
@@ -42,67 +44,54 @@ describe('Step', () => {
wrapper
.
destroy
();
});
describe
(
'
isActive
'
,
()
=>
{
it
(
'
should
return true
when this step is the current step
'
,
()
=>
{
describe
(
'
Step Body
'
,
()
=>
{
it
(
'
should
display the step body
when this step is the current step
'
,
()
=>
{
factory
();
expect
(
wrapper
.
vm
.
isActive
).
toEqual
(
true
);
expect
(
wrapper
.
find
(
'
.card > div
'
).
attributes
(
'
style
'
)).
toBeUndefined
(
);
});
it
(
'
should
return false
when this step is not the current step
'
,
()
=>
{
it
(
'
should
not display the step body
when this step is not the current step
'
,
()
=>
{
activatePreviousStep
();
factory
();
expect
(
wrapper
.
vm
.
isActive
).
toEqual
(
false
);
expect
(
wrapper
.
find
(
'
.card > div
'
).
attributes
(
'
style
'
)).
toBe
(
'
display: none;
'
);
});
});
describe
(
'
isFinished
'
,
()
=>
{
it
(
'
should
return true
when this step is valid and not active
'
,
()
=>
{
describe
(
'
Step Summary
'
,
()
=>
{
it
(
'
should
be shown
when this step is valid and not active
'
,
()
=>
{
activatePreviousStep
();
factory
();
expect
(
wrapper
.
vm
.
isFinished
).
toEqual
(
true
);
expect
(
wrapper
.
find
(
StepSummary
).
exists
()).
toBe
(
true
);
});
it
(
'
should
return false
when this step is not valid and not active
'
,
()
=>
{
it
(
'
should
not be shown
when this step is not valid and not active
'
,
()
=>
{
activatePreviousStep
();
factory
({
isValid
:
false
});
expect
(
wrapper
.
vm
.
isFinished
).
toEqual
(
false
);
expect
(
wrapper
.
find
(
StepSummary
).
exists
()).
toBe
(
false
);
});
it
(
'
should
return false
when this step is valid and active
'
,
()
=>
{
it
(
'
should
not be shown
when this step is valid and active
'
,
()
=>
{
factory
();
expect
(
wrapper
.
vm
.
isFinished
).
toEqual
(
false
);
expect
(
wrapper
.
find
(
StepSummary
).
exists
()).
toBe
(
false
);
});
it
(
'
should
return false
when this step is not valid and active
'
,
()
=>
{
it
(
'
should
not be shown
when this step is not valid and active
'
,
()
=>
{
factory
({
isValid
:
false
});
expect
(
wrapper
.
vm
.
isFinished
).
toEqual
(
false
);
expect
(
wrapper
.
find
(
StepSummary
).
exists
()).
toBe
(
false
);
});
});
describe
(
'
editable
'
,
()
=>
{
it
(
'
should return true when this step is finished and comes before the current step
'
,
()
=>
{
factory
({
step
:
'
firstStep
'
});
expect
(
wrapper
.
vm
.
editable
).
toEqual
(
true
);
});
it
(
'
should return false when this step is not finished and comes before the current step
'
,
()
=>
{
factory
({
step
:
'
firstStep
'
,
isValid
:
false
});
expect
(
wrapper
.
vm
.
editable
).
toEqual
(
false
);
});
it
(
'
should return false when this step is finished and does not come before the current step
'
,
()
=>
{
activatePreviousStep
();
describe
(
'
isEditable
'
,
()
=>
{
it
(
'
should set the isEditable property to true when this step is finished and comes before the current step
'
,
()
=>
{
factory
({
step
:
'
firstStep
'
});
expect
(
wrapper
.
vm
.
editable
).
toEqual
(
fals
e
);
expect
(
wrapper
.
find
(
StepSummary
).
props
(
'
isEditable
'
)).
toBe
(
tru
e
);
});
});
...
...
@@ -125,13 +114,13 @@ describe('Step', () => {
it
(
'
shows the next button when the text was passed
'
,
()
=>
{
factory
();
expect
(
wrapper
.
text
()).
to
Equal
(
'
next
'
);
expect
(
wrapper
.
text
()).
to
Be
(
'
next
'
);
});
it
(
'
does not show the next button when no text was passed
'
,
()
=>
{
factory
({
nextStepButtonText
:
''
});
expect
(
wrapper
.
text
()).
to
Equal
(
''
);
expect
(
wrapper
.
text
()).
to
Be
(
''
);
});
it
(
'
is disabled when this step is not valid
'
,
()
=>
{
...
...
ee/spec/frontend/subscriptions/new/components/checkout/subscription_details_spec.js
0 → 100644
View file @
1d1836af
import
Vuex
from
'
vuex
'
;
import
{
mount
,
createLocalVue
}
from
'
@vue/test-utils
'
;
import
createStore
from
'
ee/subscriptions/new/store
'
;
import
*
as
types
from
'
ee/subscriptions/new/store/mutation_types
'
;
import
Step
from
'
ee/subscriptions/new/components/checkout/components/step.vue
'
;
import
Component
from
'
ee/subscriptions/new/components/checkout/subscription_details.vue
'
;
describe
(
'
Subscription Details
'
,
()
=>
{
const
localVue
=
createLocalVue
();
localVue
.
use
(
Vuex
);
let
wrapper
;
const
planData
=
[
{
id
:
'
firstPlanId
'
,
code
:
'
bronze
'
,
price_per_year
:
48
},
{
id
:
'
secondPlanId
'
,
code
:
'
silver
'
,
price_per_year
:
228
},
];
const
initialData
=
{
planData
:
JSON
.
stringify
(
planData
),
planId
:
'
secondPlanId
'
,
setupForCompany
:
'
true
'
,
fullName
:
'
Full Name
'
,
};
const
store
=
createStore
(
initialData
);
const
createComponent
=
(
opts
=
{})
=>
{
wrapper
=
mount
(
Component
,
{
localVue
,
sync
:
false
,
store
,
...
opts
,
});
};
beforeEach
(()
=>
{
createComponent
();
});
afterEach
(()
=>
{
wrapper
.
destroy
();
});
const
isStepValid
=
()
=>
wrapper
.
find
(
Step
).
props
(
'
isValid
'
);
describe
(
'
Setting up for personal use
'
,
()
=>
{
beforeEach
(()
=>
{
store
.
commit
(
types
.
UPDATE_IS_SETUP_FOR_COMPANY
,
false
);
store
.
commit
(
types
.
UPDATE_NUMBER_OF_USERS
,
1
);
});
it
(
'
should be valid
'
,
()
=>
{
expect
(
isStepValid
()).
toBe
(
true
);
});
it
(
'
should not display an input field for the company or group name
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
#organizationName
'
).
exists
()).
toBe
(
false
);
});
it
(
'
should disable the number of users input field
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
#numberOfUsers
'
).
attributes
(
'
disabled
'
)).
toBeDefined
();
});
it
(
'
should show a link to change to setting up for a company
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.company-link
'
).
exists
()).
toBe
(
true
);
});
});
describe
(
'
Setting up for a company or group
'
,
()
=>
{
beforeEach
(()
=>
{
store
.
commit
(
types
.
UPDATE_IS_SETUP_FOR_COMPANY
,
true
);
store
.
commit
(
types
.
UPDATE_NUMBER_OF_USERS
,
0
);
});
it
(
'
should be invalid
'
,
()
=>
{
expect
(
isStepValid
()).
toBe
(
false
);
});
it
(
'
should display an input field for the company or group name
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
#organizationName
'
).
exists
()).
toBe
(
true
);
});
it
(
'
should enable the number of users input field
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
#numberOfUsers
'
).
attributes
(
'
disabled
'
)).
toBeUndefined
();
});
it
(
'
should not show the link to change to setting up for a company
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.company-link
'
).
exists
()).
toBe
(
false
);
});
describe
(
'
filling in the company name and the number of users
'
,
()
=>
{
it
(
'
should make the component valid
'
,
()
=>
{
store
.
commit
(
types
.
UPDATE_ORGANIZATION_NAME
,
'
My Organization
'
);
store
.
commit
(
types
.
UPDATE_NUMBER_OF_USERS
,
2
);
return
localVue
.
nextTick
().
then
(()
=>
{
expect
(
isStepValid
()).
toBe
(
true
);
});
});
});
});
describe
(
'
Showing summary
'
,
()
=>
{
beforeEach
(()
=>
{
store
.
commit
(
types
.
UPDATE_IS_SETUP_FOR_COMPANY
,
true
);
store
.
commit
(
types
.
UPDATE_SELECTED_PLAN
,
'
firstPlanId
'
);
store
.
commit
(
types
.
UPDATE_ORGANIZATION_NAME
,
'
My Organization
'
);
store
.
commit
(
types
.
UPDATE_NUMBER_OF_USERS
,
25
);
store
.
commit
(
types
.
UPDATE_CURRENT_STEP
,
'
nextStep
'
);
});
it
(
'
should show the selected plan
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-summary-line-1
'
).
text
()).
toEqual
(
'
Bronze plan
'
);
});
it
(
'
should show the entered group name
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-summary-line-2
'
).
text
()).
toEqual
(
'
Group: My Organization
'
);
});
it
(
'
should show the entered number of users
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-summary-line-3
'
).
text
()).
toEqual
(
'
Users: 25
'
);
});
});
});
ee/spec/frontend/subscriptions/new/store/actions_spec.js
View file @
1d1836af
...
...
@@ -11,7 +11,7 @@ describe('Subscriptions Actions', () => {
actions
.
activateStep
,
'
secondStep
'
,
{},
[{
type
:
'
ACTIVATE
_STEP
'
,
payload
:
'
secondStep
'
}],
[{
type
:
'
UPDATE_CURRENT
_STEP
'
,
payload
:
'
secondStep
'
}],
[],
done
,
);
...
...
@@ -27,15 +27,78 @@ describe('Subscriptions Actions', () => {
testAction
(
actions
.
activateNextStep
,
{},
{
active
StepIndex
:
0
},
[{
type
:
'
ACTIVATE
_STEP
'
,
payload
:
'
secondStep
'
}],
{
current
StepIndex
:
0
},
[{
type
:
'
UPDATE_CURRENT
_STEP
'
,
payload
:
'
secondStep
'
}],
[],
done
,
);
});
it
(
'
does not change the currentStep if the current step is the last step
'
,
done
=>
{
testAction
(
actions
.
activateNextStep
,
{},
{
activeStepIndex
:
1
},
[],
[],
done
);
testAction
(
actions
.
activateNextStep
,
{},
{
currentStepIndex
:
1
},
[],
[],
done
);
});
});
describe
(
'
updateSelectedPlan
'
,
()
=>
{
it
(
'
updates the selected plan
'
,
done
=>
{
testAction
(
actions
.
updateSelectedPlan
,
'
planId
'
,
{},
[{
type
:
'
UPDATE_SELECTED_PLAN
'
,
payload
:
'
planId
'
}],
[],
done
,
);
});
});
describe
(
'
toggleIsSetupForCompany
'
,
()
=>
{
it
(
'
toggles the isSetupForCompany value
'
,
done
=>
{
testAction
(
actions
.
toggleIsSetupForCompany
,
{},
{
isSetupForCompany
:
true
},
[{
type
:
'
UPDATE_IS_SETUP_FOR_COMPANY
'
,
payload
:
false
}],
[],
done
,
);
});
});
describe
(
'
updateNumberOfUsers
'
,
()
=>
{
it
(
'
updates numberOfUsers to 0 when no value is provided
'
,
done
=>
{
testAction
(
actions
.
updateNumberOfUsers
,
null
,
{},
[{
type
:
'
UPDATE_NUMBER_OF_USERS
'
,
payload
:
0
}],
[],
done
,
);
});
it
(
'
updates numberOfUsers when a value is provided
'
,
done
=>
{
testAction
(
actions
.
updateNumberOfUsers
,
2
,
{},
[{
type
:
'
UPDATE_NUMBER_OF_USERS
'
,
payload
:
2
}],
[],
done
,
);
});
});
describe
(
'
updateOrganizationName
'
,
()
=>
{
it
(
'
updates organizationName to the provided value
'
,
done
=>
{
testAction
(
actions
.
updateOrganizationName
,
'
name
'
,
{},
[{
type
:
'
UPDATE_ORGANIZATION_NAME
'
,
payload
:
'
name
'
}],
[],
done
,
);
});
});
});
ee/spec/frontend/subscriptions/new/store/getters_spec.js
View file @
1d1836af
...
...
@@ -5,6 +5,14 @@ constants.STEPS = ['firstStep', 'secondStep'];
const
state
=
{
currentStep
:
'
secondStep
'
,
isSetupForCompany
:
true
,
availablePlans
:
[
{
value
:
'
firstPlan
'
,
text
:
'
first plan
'
,
},
],
selectedPlan
:
'
firstPlan
'
,
};
describe
(
'
Subscriptions Getters
'
,
()
=>
{
...
...
@@ -24,16 +32,33 @@ describe('Subscriptions Getters', () => {
});
});
describe
(
'
active
StepIndex
'
,
()
=>
{
describe
(
'
current
StepIndex
'
,
()
=>
{
it
(
'
returns a function
'
,
()
=>
{
expect
(
getters
.
active
StepIndex
(
state
,
getters
)).
toBeInstanceOf
(
Function
);
expect
(
getters
.
current
StepIndex
(
state
,
getters
)).
toBeInstanceOf
(
Function
);
});
it
(
'
calls the stepIndex function with the current step name
'
,
()
=>
{
const
stepIndexSpy
=
jest
.
spyOn
(
getters
,
'
stepIndex
'
);
getters
.
active
StepIndex
(
state
,
getters
);
getters
.
current
StepIndex
(
state
,
getters
);
expect
(
stepIndexSpy
).
toHaveBeenCalledWith
(
'
secondStep
'
);
});
});
describe
(
'
selectedPlanText
'
,
()
=>
{
it
(
'
returns the text for selectedPlan
'
,
()
=>
{
expect
(
getters
.
selectedPlanText
(
state
,
{
selectedPlanDetails
:
{
text
:
'
selected plan
'
}
}),
).
toEqual
(
'
selected plan
'
);
});
});
describe
(
'
selectedPlanDetails
'
,
()
=>
{
it
(
'
returns the details for the selected plan
'
,
()
=>
{
expect
(
getters
.
selectedPlanDetails
(
state
)).
toEqual
({
value
:
'
firstPlan
'
,
text
:
'
first plan
'
,
});
});
});
});
ee/spec/frontend/subscriptions/new/store/mutations_spec.js
View file @
1d1836af
...
...
@@ -3,6 +3,10 @@ import * as types from 'ee/subscriptions/new/store/mutation_types';
const
state
=
()
=>
({
currentStep
:
'
firstStep
'
,
selectedPlan
:
'
firstPlan
'
,
isSetupForCompany
:
true
,
numberOfUsers
:
1
,
organizationName
:
'
name
'
,
});
let
stateCopy
;
...
...
@@ -11,10 +15,42 @@ beforeEach(() => {
stateCopy
=
state
();
});
describe
(
'
ACTIVATE
_STEP
'
,
()
=>
{
describe
(
'
UPDATE_CURRENT
_STEP
'
,
()
=>
{
it
(
'
should set the currentStep to the given step
'
,
()
=>
{
mutations
[
types
.
ACTIVATE
_STEP
](
stateCopy
,
'
secondStep
'
);
mutations
[
types
.
UPDATE_CURRENT
_STEP
](
stateCopy
,
'
secondStep
'
);
expect
(
stateCopy
.
currentStep
).
toEqual
(
'
secondStep
'
);
});
});
describe
(
'
UPDATE_SELECTED_PLAN
'
,
()
=>
{
it
(
'
should set the selectedPlan to the given plan
'
,
()
=>
{
mutations
[
types
.
UPDATE_SELECTED_PLAN
](
stateCopy
,
'
secondPlan
'
);
expect
(
stateCopy
.
selectedPlan
).
toEqual
(
'
secondPlan
'
);
});
});
describe
(
'
UPDATE_IS_SETUP_FOR_COMPANY
'
,
()
=>
{
it
(
'
should set the isSetupForCompany to the given boolean
'
,
()
=>
{
mutations
[
types
.
UPDATE_IS_SETUP_FOR_COMPANY
](
stateCopy
,
false
);
expect
(
stateCopy
.
isSetupForCompany
).
toEqual
(
false
);
});
});
describe
(
'
UPDATE_NUMBER_OF_USERS
'
,
()
=>
{
it
(
'
should set the numberOfUsers to the given number
'
,
()
=>
{
mutations
[
types
.
UPDATE_NUMBER_OF_USERS
](
stateCopy
,
2
);
expect
(
stateCopy
.
numberOfUsers
).
toEqual
(
2
);
});
});
describe
(
'
UPDATE_ORGANIZATION_NAME
'
,
()
=>
{
it
(
'
should set the organizationName to the given name
'
,
()
=>
{
mutations
[
types
.
UPDATE_ORGANIZATION_NAME
](
stateCopy
,
'
new name
'
);
expect
(
stateCopy
.
organizationName
).
toEqual
(
'
new name
'
);
});
});
ee/spec/frontend/subscriptions/new/store/state_spec.js
0 → 100644
View file @
1d1836af
import
createState
from
'
ee/subscriptions/new/store/state
'
;
import
*
as
constants
from
'
ee/subscriptions/new/constants
'
;
constants
.
STEPS
=
[
'
firstStep
'
,
'
secondStep
'
];
describe
(
'
projectsSelector default state
'
,
()
=>
{
const
planData
=
[
{
id
:
'
firstPlanId
'
,
code
:
'
bronze
'
,
price_per_year
:
48
},
{
id
:
'
secondPlanId
'
,
code
:
'
silver
'
,
price_per_year
:
228
},
];
const
initialData
=
{
planData
:
JSON
.
stringify
(
planData
),
planId
:
'
secondPlanId
'
,
setupForCompany
:
'
true
'
,
fullName
:
'
Full Name
'
,
};
const
state
=
createState
(
initialData
);
it
(
'
sets the currentStep to the first item of the STEPS constant
'
,
()
=>
{
expect
(
state
.
currentStep
).
toEqual
(
'
firstStep
'
);
});
describe
(
'
availablePlans
'
,
()
=>
{
it
(
'
sets the availablePlans to the provided parsed planData
'
,
()
=>
{
expect
(
state
.
availablePlans
).
toEqual
([
{
value
:
'
firstPlanId
'
,
text
:
'
Bronze
'
,
pricePerUserPerYear
:
48
},
{
value
:
'
secondPlanId
'
,
text
:
'
Silver
'
,
pricePerUserPerYear
:
228
},
]);
});
it
(
'
sets the availablePlans to an empty array when no planData provided
'
,
()
=>
{
const
modifiedState
=
createState
({
...
initialData
,
...{
planData
:
undefined
}
});
expect
(
modifiedState
.
availablePlans
).
toEqual
([]);
});
});
describe
(
'
selectedPlan
'
,
()
=>
{
it
(
'
sets the selectedPlan to the provided planId if it is present in the provided planData
'
,
()
=>
{
expect
(
state
.
selectedPlan
).
toEqual
(
'
secondPlanId
'
);
});
it
(
'
sets the selectedPlan to the first value of availablePlans if planId is not provided
'
,
()
=>
{
const
modifiedState
=
createState
({
...
initialData
,
...{
planId
:
undefined
}
});
expect
(
modifiedState
.
selectedPlan
).
toEqual
(
'
firstPlanId
'
);
});
it
(
'
sets the selectedPlan to the first value of availablePlans if planId is not present in the availablePlans
'
,
()
=>
{
const
modifiedState
=
createState
({
...
initialData
,
...{
planId
:
'
invalidPlanId
'
}
});
expect
(
modifiedState
.
selectedPlan
).
toEqual
(
'
firstPlanId
'
);
});
it
(
'
sets the selectedPlan to an empty string if availablePlans are not present
'
,
()
=>
{
const
modifiedState
=
createState
({
...
initialData
,
...{
planData
:
'
[]
'
}
});
expect
(
modifiedState
.
selectedPlan
).
toBeUndefined
();
});
});
describe
(
'
isSetupForCompany
'
,
()
=>
{
it
(
'
sets the isSetupForCompany to true if provided setupForCompany is "true"
'
,
()
=>
{
expect
(
state
.
isSetupForCompany
).
toEqual
(
true
);
});
it
(
'
sets the isSetupForCompany to false if provided setupForCompany is "false"
'
,
()
=>
{
const
modifiedState
=
createState
({
...
initialData
,
...{
setupForCompany
:
'
false
'
}
});
expect
(
modifiedState
.
isSetupForCompany
).
toEqual
(
false
);
});
});
it
(
'
sets the fullName to the provided fullName
'
,
()
=>
{
expect
(
state
.
fullName
).
toEqual
(
'
Full Name
'
);
});
it
(
'
sets the organizationName to null
'
,
()
=>
{
expect
(
state
.
organizationName
).
toBeNull
();
});
describe
(
'
numberOfUsers
'
,
()
=>
{
it
(
'
sets the numberOfUsers to 0 when setupForCompany is true
'
,
()
=>
{
expect
(
state
.
numberOfUsers
).
toEqual
(
0
);
});
it
(
'
sets the numberOfUsers to 1 when setupForCompany is false
'
,
()
=>
{
const
modifiedState
=
createState
({
...
initialData
,
...{
setupForCompany
:
'
false
'
}
});
expect
(
modifiedState
.
numberOfUsers
).
toEqual
(
1
);
});
});
});
locale/gitlab.pot
View file @
1d1836af
...
...
@@ -3263,6 +3263,9 @@ msgstr ""
msgid "Checkout"
msgstr ""
msgid "Checkout|%{selectedPlanText} plan"
msgstr ""
msgid "Checkout|1. Your profile"
msgstr ""
...
...
@@ -3275,9 +3278,36 @@ msgstr ""
msgid "Checkout|Checkout"
msgstr ""
msgid "Checkout|Continue to billing"
msgstr ""
msgid "Checkout|Edit"
msgstr ""
msgid "Checkout|GitLab plan"
msgstr ""
msgid "Checkout|Group"
msgstr ""
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
msgid "Checkout|Need more users? Purchase GitLab for your %{company}."
msgstr ""
msgid "Checkout|Number of users"
msgstr ""
msgid "Checkout|Subscription details"
msgstr ""
msgid "Checkout|Users"
msgstr ""
msgid "Checkout|company or team"
msgstr ""
msgid "Cherry-pick this commit"
msgstr ""
...
...
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