Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
95a435ab
Commit
95a435ab
authored
Nov 10, 2014
by
Will Daly
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Include course ID in analytics events for logistration
parent
a13f4f2f
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
573 additions
and
449 deletions
+573
-449
common/djangoapps/user_api/helpers.py
+14
-0
common/djangoapps/user_api/tests/test_helpers.py
+11
-0
common/djangoapps/user_api/views.py
+15
-1
lms/static/js/spec/student_account/login_spec.js
+202
-173
lms/static/js/spec/student_account/register_spec.js
+297
-267
lms/static/js/student_account/models/LoginModel.js
+17
-4
lms/static/js/student_account/models/RegisterModel.js
+17
-4
No files found.
common/djangoapps/user_api/helpers.py
View file @
95a435ab
...
@@ -343,6 +343,20 @@ def shim_student_view(view_func, check_logged_in=False):
...
@@ -343,6 +343,20 @@ def shim_student_view(view_func, check_logged_in=False):
if
"course_id"
in
request
.
POST
:
if
"course_id"
in
request
.
POST
:
del
request
.
POST
[
"course_id"
]
del
request
.
POST
[
"course_id"
]
# Include the course ID if it's specified in the analytics info
# so it can be included in analytics events.
if
"analytics"
in
request
.
POST
:
try
:
analytics
=
json
.
loads
(
request
.
POST
[
"analytics"
])
if
"enroll_course_id"
in
analytics
:
request
.
POST
[
"course_id"
]
=
analytics
.
get
(
"enroll_course_id"
)
except
(
ValueError
,
TypeError
):
LOGGER
.
error
(
u"Could not parse analytics object sent to user API: {analytics}"
.
format
(
analytics
=
analytics
)
)
# Backwards compatibility: the student view expects both
# Backwards compatibility: the student view expects both
# terms of service and honor code values. Since we're combining
# terms of service and honor code values. Since we're combining
# these into a single checkbox, the only value we may get
# these into a single checkbox, the only value we may get
...
...
common/djangoapps/user_api/tests/test_helpers.py
View file @
95a435ab
...
@@ -150,6 +150,17 @@ class StudentViewShimTest(TestCase):
...
@@ -150,6 +150,17 @@ class StudentViewShimTest(TestCase):
self
.
assertNotIn
(
"enrollment_action"
,
self
.
captured_request
.
POST
)
self
.
assertNotIn
(
"enrollment_action"
,
self
.
captured_request
.
POST
)
self
.
assertNotIn
(
"course_id"
,
self
.
captured_request
.
POST
)
self
.
assertNotIn
(
"course_id"
,
self
.
captured_request
.
POST
)
def
test_include_analytics_info
(
self
):
view
=
self
.
_shimmed_view
(
HttpResponse
())
request
=
HttpRequest
()
request
.
POST
[
"analytics"
]
=
json
.
dumps
({
"enroll_course_id"
:
"edX/DemoX/Fall"
})
view
(
request
)
# Expect that the analytics course ID was passed to the view
self
.
assertEqual
(
self
.
captured_request
.
POST
.
get
(
"course_id"
),
"edX/DemoX/Fall"
)
def
test_third_party_auth_login_failure
(
self
):
def
test_third_party_auth_login_failure
(
self
):
view
=
self
.
_shimmed_view
(
view
=
self
.
_shimmed_view
(
HttpResponse
(
status
=
403
),
HttpResponse
(
status
=
403
),
...
...
common/djangoapps/user_api/views.py
View file @
95a435ab
...
@@ -133,6 +133,13 @@ class LoginSessionView(APIView):
...
@@ -133,6 +133,13 @@ class LoginSessionView(APIView):
def
post
(
self
,
request
):
def
post
(
self
,
request
):
"""Log in a user.
"""Log in a user.
You must send all required form fields with the request.
You can optionally send an `analytics` param with a JSON-encoded
object with additional info to include in the login analytics event.
Currently, the only supported field is "enroll_course_id" to indicate
that the user logged in while enrolling in a particular course.
Arguments:
Arguments:
request (HttpRequest)
request (HttpRequest)
...
@@ -148,7 +155,7 @@ class LoginSessionView(APIView):
...
@@ -148,7 +155,7 @@ class LoginSessionView(APIView):
Example Usage:
Example Usage:
POST /user_api/v1/login_session
POST /user_api/v1/login_session
with POST params `email`
and `password`
with POST params `email`
, `password`, and `remember`.
200 OK
200 OK
...
@@ -246,6 +253,13 @@ class RegistrationView(APIView):
...
@@ -246,6 +253,13 @@ class RegistrationView(APIView):
def
post
(
self
,
request
):
def
post
(
self
,
request
):
"""Create the user's account.
"""Create the user's account.
You must send all required form fields with the request.
You can optionally send an `analytics` param with a JSON-encoded
object with additional info to include in the registration analytics event.
Currently, the only supported field is "enroll_course_id" to indicate
that the user registered while enrolling in a particular course.
Arguments:
Arguments:
request (HTTPRequest)
request (HTTPRequest)
...
...
lms/static/js/spec/student_account/login_spec.js
View file @
95a435ab
...
@@ -6,208 +6,237 @@ define([
...
@@ -6,208 +6,237 @@ define([
'js/student_account/models/LoginModel'
,
'js/student_account/models/LoginModel'
,
'js/student_account/views/LoginView'
'js/student_account/views/LoginView'
],
function
(
$
,
_
,
TemplateHelpers
,
AjaxHelpers
,
LoginModel
,
LoginView
)
{
],
function
(
$
,
_
,
TemplateHelpers
,
AjaxHelpers
,
LoginModel
,
LoginView
)
{
describe
(
'edx.student.account.LoginView'
,
function
()
{
'use strict'
;
'use strict'
;
describe
(
'edx.student.account.LoginView'
,
function
()
{
var
model
=
null
,
var
model
=
null
,
view
=
null
,
view
=
null
,
requests
=
null
,
requests
=
null
,
authComplete
=
false
,
authComplete
=
false
,
PLATFORM_NAME
=
'edX'
,
PLATFORM_NAME
=
'edX'
,
USER_DATA
=
{
USER_DATA
=
{
email
:
'xsy@edx.org'
,
email
:
'xsy@edx.org'
,
password
:
'xsyisawesome'
,
password
:
'xsyisawesome'
,
remember
:
true
remember
:
true
},
},
THIRD_PARTY_AUTH
=
{
THIRD_PARTY_AUTH
=
{
currentProvider
:
null
,
currentProvider
:
null
,
providers
:
[
providers
:
[
{
{
name
:
'Google'
,
name
:
'Google'
,
iconClass
:
'icon-google-plus'
,
iconClass
:
'icon-google-plus'
,
loginUrl
:
'/auth/login/google-oauth2/?auth_entry=account_login'
,
loginUrl
:
'/auth/login/google-oauth2/?auth_entry=account_login'
,
registerUrl
:
'/auth/login/google-oauth2/?auth_entry=account_register'
registerUrl
:
'/auth/login/google-oauth2/?auth_entry=account_register'
},
},
{
{
name
:
'Facebook'
,
name
:
'Facebook'
,
iconClass
:
'icon-facebook'
,
iconClass
:
'icon-facebook'
,
loginUrl
:
'/auth/login/facebook/?auth_entry=account_login'
,
loginUrl
:
'/auth/login/facebook/?auth_entry=account_login'
,
registerUrl
:
'/auth/login/facebook/?auth_entry=account_register'
registerUrl
:
'/auth/login/facebook/?auth_entry=account_register'
}
}
]
]
},
},
FORM_DESCRIPTION
=
{
FORM_DESCRIPTION
=
{
method
:
'post'
,
method
:
'post'
,
submit_url
:
'/user_api/v1/account/login_session/'
,
submit_url
:
'/user_api/v1/account/login_session/'
,
fields
:
[
fields
:
[
{
{
name
:
'email'
,
name
:
'email'
,
label
:
'Email'
,
label
:
'Email'
,
defaultValue
:
''
,
defaultValue
:
''
,
type
:
'email'
,
type
:
'email'
,
required
:
true
,
required
:
true
,
placeholder
:
'place@holder.org'
,
placeholder
:
'place@holder.org'
,
instructions
:
'Enter your email.'
,
instructions
:
'Enter your email.'
,
restrictions
:
{}
restrictions
:
{}
},
},
{
{
name
:
'password'
,
name
:
'password'
,
label
:
'Password'
,
label
:
'Password'
,
defaultValue
:
''
,
defaultValue
:
''
,
type
:
'password'
,
type
:
'password'
,
required
:
true
,
required
:
true
,
instructions
:
'Enter your password.'
,
instructions
:
'Enter your password.'
,
restrictions
:
{}
restrictions
:
{}
},
},
{
{
name
:
'remember'
,
name
:
'remember'
,
label
:
'Remember me'
,
label
:
'Remember me'
,
defaultValue
:
''
,
defaultValue
:
''
,
type
:
'checkbox'
,
type
:
'checkbox'
,
required
:
true
,
required
:
true
,
instructions
:
"Agree to the terms of service."
,
instructions
:
"Agree to the terms of service."
,
restrictions
:
{}
restrictions
:
{}
}
}
]
]
};
},
COURSE_ID
=
"edX/demoX/Fall"
;
var
createLoginView
=
function
(
test
)
{
// Initialize the login model
var
createLoginView
=
function
(
test
)
{
model
=
new
LoginModel
({},
{
// Initialize the login model
url
:
FORM_DESCRIPTION
.
submit_url
,
model
=
new
LoginModel
({},
{
method
:
FORM_DESCRIPTION
.
method
url
:
FORM_DESCRIPTION
.
submit_url
,
});
method
:
FORM_DESCRIPTION
.
method
});
// Initialize the login view
// Initialize the login view
view
=
new
LoginView
({
view
=
new
LoginView
({
fields
:
FORM_DESCRIPTION
.
fields
,
fields
:
FORM_DESCRIPTION
.
fields
,
model
:
model
,
model
:
model
,
thirdPartyAuth
:
THIRD_PARTY_AUTH
,
thirdPartyAuth
:
THIRD_PARTY_AUTH
,
platformName
:
PLATFORM_NAME
platformName
:
PLATFORM_NAME
});
});
// Spy on AJAX requests
// Spy on AJAX requests
requests
=
AjaxHelpers
.
requests
(
test
);
requests
=
AjaxHelpers
.
requests
(
test
);
// Intercept events from the view
// Intercept events from the view
authComplete
=
false
;
authComplete
=
false
;
view
.
on
(
"auth-complete"
,
function
()
{
view
.
on
(
"auth-complete"
,
function
()
{
authComplete
=
true
;
authComplete
=
true
;
});
};
var
submitForm
=
function
(
validationSuccess
)
{
// Simulate manual entry of login form data
$
(
'#login-email'
).
val
(
USER_DATA
.
email
);
$
(
'#login-password'
).
val
(
USER_DATA
.
password
);
// Check the "Remember me" checkbox
$
(
'#login-remember'
).
prop
(
'checked'
,
USER_DATA
.
remember
);
// Create a fake click event
var
clickEvent
=
$
.
Event
(
'click'
);
// If validationSuccess isn't passed, we avoid
// spying on `view.validate` twice
if
(
!
_
.
isUndefined
(
validationSuccess
)
)
{
// Force validation to return as expected
spyOn
(
view
,
'validate'
).
andReturn
({
isValid
:
validationSuccess
,
message
:
'Submission was validated.'
});
});
};
}
var
submitForm
=
function
(
validationSuccess
)
{
// Simulate manual entry of login form data
$
(
'#login-email'
).
val
(
USER_DATA
.
email
);
$
(
'#login-password'
).
val
(
USER_DATA
.
password
);
// Check the "Remember me" checkbox
$
(
'#login-remember'
).
prop
(
'checked'
,
USER_DATA
.
remember
);
// Create a fake click event
var
clickEvent
=
$
.
Event
(
'click'
);
// If validationSuccess isn't passed, we avoid
// spying on `view.validate` twice
if
(
!
_
.
isUndefined
(
validationSuccess
)
)
{
// Force validation to return as expected
spyOn
(
view
,
'validate'
).
andReturn
({
isValid
:
validationSuccess
,
message
:
'Submission was validated.'
});
}
// Submit the email address
// Submit the email address
view
.
submitForm
(
clickEvent
);
view
.
submitForm
(
clickEvent
);
};
};
beforeEach
(
function
()
{
beforeEach
(
function
()
{
setFixtures
(
'<div id="login-form"></div>'
);
setFixtures
(
'<div id="login-form"></div>'
);
TemplateHelpers
.
installTemplate
(
'templates/student_account/login'
);
TemplateHelpers
.
installTemplate
(
'templates/student_account/login'
);
TemplateHelpers
.
installTemplate
(
'templates/student_account/form_field'
);
TemplateHelpers
.
installTemplate
(
'templates/student_account/form_field'
);
});
});
it
(
'logs the user in'
,
function
()
{
it
(
'logs the user in'
,
function
()
{
createLoginView
(
this
);
createLoginView
(
this
);
// Submit the form, with successful validation
// Submit the form, with successful validation
submitForm
(
true
);
submitForm
(
true
);
// Verify that the client contacts the server with the expected data
// Verify that the client contacts the server with the expected data
AjaxHelpers
.
expectRequest
(
AjaxHelpers
.
expectRequest
(
requests
,
'POST'
,
requests
,
'POST'
,
FORM_DESCRIPTION
.
submit_url
,
FORM_DESCRIPTION
.
submit_url
,
$
.
param
(
USER_DATA
)
$
.
param
(
USER_DATA
)
);
);
// Respond with status code 200
// Respond with status code 200
AjaxHelpers
.
respondWithJson
(
requests
,
{});
AjaxHelpers
.
respondWithJson
(
requests
,
{});
// Verify that auth-complete is triggered
// Verify that auth-complete is triggered
expect
(
authComplete
).
toBe
(
true
);
expect
(
authComplete
).
toBe
(
true
);
});
});
it
(
'displays third-party auth login buttons
'
,
function
()
{
it
(
'sends analytics info containing the enrolled course ID
'
,
function
()
{
createLoginView
(
this
);
createLoginView
(
this
);
// Verify that Google and Facebook registration buttons are displayed
// Simulate that the user is attempting to enroll in a course
expect
(
$
(
'.button-Google'
)).
toBeVisible
();
// by setting the course_id query string param.
expect
(
$
(
'.button-Facebook'
)).
toBeVisible
();
spyOn
(
$
,
'url'
).
andCallFake
(
function
(
param
)
{
if
(
param
===
"?course_id"
)
{
return
encodeURIComponent
(
COURSE_ID
);
}
});
});
it
(
'displays a link to the password reset form'
,
function
()
{
// Attempt to login
createLoginView
(
this
);
submitForm
(
true
);
// Verify that the password reset link is displayed
// Verify that the client sent the course ID for analytics
expect
(
$
(
'.forgot-password'
)).
toBeVisible
();
var
expectedData
=
{};
$
.
extend
(
expectedData
,
USER_DATA
,
{
analytics
:
JSON
.
stringify
({
enroll_course_id
:
COURSE_ID
})
});
});
it
(
'validates login form fields'
,
function
()
{
AjaxHelpers
.
expectRequest
(
createLoginView
(
this
);
requests
,
'POST'
,
FORM_DESCRIPTION
.
submit_url
,
$
.
param
(
expectedData
)
);
});
it
(
'displays third-party auth login buttons'
,
function
()
{
createLoginView
(
this
);
submitForm
(
true
);
// Verify that Google and Facebook registration buttons are displayed
expect
(
$
(
'.button-Google'
)).
toBeVisible
();
expect
(
$
(
'.button-Facebook'
)).
toBeVisible
();
});
// Verify that validation of form fields occurred
it
(
'displays a link to the password reset form'
,
function
()
{
expect
(
view
.
validate
).
toHaveBeenCalledWith
(
$
(
'#login-email'
)[
0
]);
createLoginView
(
this
);
expect
(
view
.
validate
).
toHaveBeenCalledWith
(
$
(
'#login-password'
)[
0
]);
});
it
(
'displays login form validation errors'
,
function
()
{
// Verify that the password reset link is displayed
createLoginView
(
this
);
expect
(
$
(
'.forgot-password'
)).
toBeVisible
();
});
// Submit the form, with failed validation
it
(
'validates login form fields'
,
function
()
{
submitForm
(
false
);
createLoginView
(
this
);
// Verify that submission errors are visible
submitForm
(
true
);
expect
(
view
.
$errors
).
not
.
toHaveClass
(
'hidden'
);
// Expect auth complete NOT to have been triggered
// Verify that validation of form fields occurred
expect
(
authComplete
).
toBe
(
false
);
expect
(
view
.
validate
).
toHaveBeenCalledWith
(
$
(
'#login-email'
)[
0
]);
});
expect
(
view
.
validate
).
toHaveBeenCalledWith
(
$
(
'#login-password'
)[
0
]);
});
it
(
'displays an error if the server returns an error while logging in
'
,
function
()
{
it
(
'displays login form validation errors
'
,
function
()
{
createLoginView
(
this
);
createLoginView
(
this
);
// Submit the form, with successful
validation
// Submit the form, with failed
validation
submitForm
(
tru
e
);
submitForm
(
fals
e
);
// Simulate an error from the LMS servers
// Verify that submission errors are visible
AjaxHelpers
.
respondWithError
(
requests
);
expect
(
view
.
$errors
).
not
.
toHaveClass
(
'hidden'
);
// Expect that an error is displayed and that auth complete is not
triggered
// Expect auth complete NOT to have been
triggered
expect
(
view
.
$errors
).
not
.
toHaveClass
(
'hidden'
);
expect
(
authComplete
).
toBe
(
false
);
expect
(
authComplete
).
toBe
(
false
);
}
);
// If we try again and succeed, the error should go away
it
(
'displays an error if the server returns an error while logging in'
,
function
()
{
submitForm
(
);
createLoginView
(
this
);
// This time, respond with status code 200
// Submit the form, with successful validation
AjaxHelpers
.
respondWithJson
(
requests
,
{}
);
submitForm
(
true
);
// Expect that the error is hidden and auth complete is triggered
// Simulate an error from the LMS servers
expect
(
view
.
$errors
).
toHaveClass
(
'hidden'
);
AjaxHelpers
.
respondWithError
(
requests
);
expect
(
authComplete
).
toBe
(
true
);
});
// Expect that an error is displayed and that auth complete is not triggered
expect
(
view
.
$errors
).
not
.
toHaveClass
(
'hidden'
);
expect
(
authComplete
).
toBe
(
false
);
// If we try again and succeed, the error should go away
submitForm
();
// This time, respond with status code 200
AjaxHelpers
.
respondWithJson
(
requests
,
{});
// Expect that the error is hidden and auth complete is triggered
expect
(
view
.
$errors
).
toHaveClass
(
'hidden'
);
expect
(
authComplete
).
toBe
(
true
);
});
});
}
}
);
);
}
);
lms/static/js/spec/student_account/register_spec.js
View file @
95a435ab
...
@@ -6,302 +6,332 @@ define([
...
@@ -6,302 +6,332 @@ define([
'js/student_account/models/RegisterModel'
,
'js/student_account/models/RegisterModel'
,
'js/student_account/views/RegisterView'
'js/student_account/views/RegisterView'
],
function
(
$
,
_
,
TemplateHelpers
,
AjaxHelpers
,
RegisterModel
,
RegisterView
)
{
],
function
(
$
,
_
,
TemplateHelpers
,
AjaxHelpers
,
RegisterModel
,
RegisterView
)
{
describe
(
'edx.student.account.RegisterView'
,
function
()
{
'use strict'
;
'use strict'
;
describe
(
'edx.student.account.RegisterView'
,
function
()
{
var
model
=
null
,
view
=
null
,
var
model
=
null
,
requests
=
null
,
view
=
null
,
authComplete
=
false
,
requests
=
null
,
PLATFORM_NAME
=
'edX'
,
authComplete
=
false
,
USER_DATA
=
{
PLATFORM_NAME
=
'edX'
,
email
:
'xsy@edx.org'
,
COURSE_ID
=
"edX/DemoX/Fall"
,
name
:
'Xsy M. Education'
,
USER_DATA
=
{
username
:
'Xsy'
,
email
:
'xsy@edx.org'
,
password
:
'xsyisawesome'
,
name
:
'Xsy M. Education'
,
level_of_education
:
'p'
,
username
:
'Xsy'
,
gender
:
'm'
,
password
:
'xsyisawesome'
,
year_of_birth
:
2014
,
level_of_education
:
'p'
,
mailing_address
:
'141 Portland'
,
gender
:
'm'
,
goals
:
'To boldly learn what no letter of the alphabet has learned before'
,
year_of_birth
:
2014
,
honor_code
:
true
mailing_address
:
'141 Portland'
,
},
goals
:
'To boldly learn what no letter of the alphabet has learned before'
,
THIRD_PARTY_AUTH
=
{
honor_code
:
true
currentProvider
:
null
,
},
providers
:
[
THIRD_PARTY_AUTH
=
{
{
currentProvider
:
null
,
name
:
'Google'
,
providers
:
[
iconClass
:
'icon-google-plus'
,
{
loginUrl
:
'/auth/login/google-oauth2/?auth_entry=account_login'
,
name
:
'Google'
,
registerUrl
:
'/auth/login/google-oauth2/?auth_entry=account_register'
iconClass
:
'icon-google-plus'
,
},
loginUrl
:
'/auth/login/google-oauth2/?auth_entry=account_login'
,
{
registerUrl
:
'/auth/login/google-oauth2/?auth_entry=account_register'
name
:
'Facebook'
,
},
iconClass
:
'icon-facebook'
,
{
loginUrl
:
'/auth/login/facebook/?auth_entry=account_login'
,
name
:
'Facebook'
,
registerUrl
:
'/auth/login/facebook/?auth_entry=account_register'
iconClass
:
'icon-facebook'
,
}
loginUrl
:
'/auth/login/facebook/?auth_entry=account_login'
,
]
registerUrl
:
'/auth/login/facebook/?auth_entry=account_register'
},
}
FORM_DESCRIPTION
=
{
]
method
:
'post'
,
},
submit_url
:
'/user_api/v1/account/registration/'
,
FORM_DESCRIPTION
=
{
fields
:
[
method
:
'post'
,
{
submit_url
:
'/user_api/v1/account/registration/'
,
name
:
'email'
,
fields
:
[
label
:
'Email'
,
{
defaultValue
:
''
,
name
:
'email'
,
type
:
'email'
,
label
:
'Email'
,
required
:
true
,
defaultValue
:
''
,
placeholder
:
'place@holder.org'
,
type
:
'email'
,
instructions
:
'Enter your email.'
,
required
:
true
,
restrictions
:
{}
placeholder
:
'place@holder.org'
,
},
instructions
:
'Enter your email.'
,
{
restrictions
:
{}
name
:
'name'
,
},
label
:
'Full Name'
,
{
defaultValue
:
''
,
name
:
'name'
,
type
:
'text'
,
label
:
'Full Name'
,
required
:
true
,
defaultValue
:
''
,
instructions
:
'Enter your username.'
,
type
:
'text'
,
restrictions
:
{}
required
:
true
,
},
instructions
:
'Enter your username.'
,
{
restrictions
:
{}
name
:
'username'
,
},
label
:
'Username'
,
{
defaultValue
:
''
,
name
:
'username'
,
type
:
'text'
,
label
:
'Username'
,
required
:
true
,
defaultValue
:
''
,
instructions
:
'Enter your username.'
,
type
:
'text'
,
restrictions
:
{}
required
:
true
,
},
instructions
:
'Enter your username.'
,
{
restrictions
:
{}
name
:
'password'
,
},
label
:
'Password'
,
{
defaultValue
:
''
,
name
:
'password'
,
type
:
'password'
,
label
:
'Password'
,
required
:
true
,
defaultValue
:
''
,
instructions
:
'Enter your password.'
,
type
:
'password'
,
restrictions
:
{}
required
:
true
,
},
instructions
:
'Enter your password.'
,
{
restrictions
:
{}
name
:
'level_of_education'
,
},
label
:
'Highest Level of Education Completed'
,
{
defaultValue
:
''
,
name
:
'level_of_education'
,
type
:
'select'
,
label
:
'Highest Level of Education Completed'
,
options
:
[
defaultValue
:
''
,
{
value
:
""
,
name
:
"--"
},
type
:
'select'
,
{
value
:
"p"
,
name
:
"Doctorate"
},
options
:
[
{
value
:
"m"
,
name
:
"Master's or professional degree"
},
{
value
:
""
,
name
:
"--"
},
{
value
:
"b"
,
name
:
"Bachelor's degree"
},
{
value
:
"p"
,
name
:
"Doctorate"
},
],
{
value
:
"m"
,
name
:
"Master's or professional degree"
},
required
:
false
,
{
value
:
"b"
,
name
:
"Bachelor's degree"
},
instructions
:
'Select your education level.'
,
],
restrictions
:
{}
required
:
false
,
},
instructions
:
'Select your education level.'
,
{
restrictions
:
{}
name
:
'gender'
,
},
label
:
'Gender'
,
{
defaultValue
:
''
,
name
:
'gender'
,
type
:
'select'
,
label
:
'Gender'
,
options
:
[
defaultValue
:
''
,
{
value
:
""
,
name
:
"--"
},
type
:
'select'
,
{
value
:
"m"
,
name
:
"Male"
},
options
:
[
{
value
:
"f"
,
name
:
"Female"
},
{
value
:
""
,
name
:
"--"
},
{
value
:
"o"
,
name
:
"Other"
},
{
value
:
"m"
,
name
:
"Male"
},
],
{
value
:
"f"
,
name
:
"Female"
},
required
:
false
,
{
value
:
"o"
,
name
:
"Other"
},
instructions
:
'Select your gender.'
,
],
restrictions
:
{}
required
:
false
,
},
instructions
:
'Select your gender.'
,
{
restrictions
:
{}
name
:
'year_of_birth'
,
},
label
:
'Year of Birth'
,
{
defaultValue
:
''
,
name
:
'year_of_birth'
,
type
:
'select'
,
label
:
'Year of Birth'
,
options
:
[
defaultValue
:
''
,
{
value
:
""
,
name
:
"--"
},
type
:
'select'
,
{
value
:
1900
,
name
:
"1900"
},
options
:
[
{
value
:
1950
,
name
:
"1950"
},
{
value
:
""
,
name
:
"--"
},
{
value
:
2014
,
name
:
"2014"
},
{
value
:
1900
,
name
:
"1900"
},
],
{
value
:
1950
,
name
:
"1950"
},
required
:
false
,
{
value
:
2014
,
name
:
"2014"
},
instructions
:
'Select your year of birth.'
,
],
restrictions
:
{}
required
:
false
,
},
instructions
:
'Select your year of birth.'
,
{
restrictions
:
{}
name
:
'mailing_address'
,
},
label
:
'Mailing Address'
,
{
defaultValue
:
''
,
name
:
'mailing_address'
,
type
:
'textarea'
,
label
:
'Mailing Address'
,
required
:
false
,
defaultValue
:
''
,
instructions
:
'Enter your mailing address.'
,
type
:
'textarea'
,
restrictions
:
{}
required
:
false
,
},
instructions
:
'Enter your mailing address.'
,
{
restrictions
:
{}
name
:
'goals'
,
},
label
:
'Goals'
,
{
defaultValue
:
''
,
name
:
'goals'
,
type
:
'textarea'
,
label
:
'Goals'
,
required
:
false
,
defaultValue
:
''
,
instructions
:
"If you'd like, tell us why you're interested in edX."
,
type
:
'textarea'
,
restrictions
:
{}
required
:
false
,
},
instructions
:
"If you'd like, tell us why you're interested in edX."
,
{
restrictions
:
{}
name
:
'honor_code'
,
},
label
:
'I agree to the <a href="/honor">Terms of Service and Honor Code</a>'
,
{
defaultValue
:
''
,
name
:
'honor_code'
,
type
:
'checkbox'
,
label
:
'I agree to the <a href="/honor">Terms of Service and Honor Code</a>'
,
required
:
true
,
defaultValue
:
''
,
instructions
:
''
,
type
:
'checkbox'
,
restrictions
:
{}
required
:
true
,
}
instructions
:
''
,
]
restrictions
:
{}
};
}
]
var
createRegisterView
=
function
(
that
)
{
};
// Initialize the register model
model
=
new
RegisterModel
({},
{
url
:
FORM_DESCRIPTION
.
submit_url
,
method
:
FORM_DESCRIPTION
.
method
});
// Initialize the register view
var
createRegisterView
=
function
(
that
)
{
view
=
new
RegisterView
({
// Initialize the register model
fields
:
FORM_DESCRIPTION
.
fields
,
model
=
new
RegisterModel
({},
{
model
:
model
,
url
:
FORM_DESCRIPTION
.
submit_url
,
thirdPartyAuth
:
THIRD_PARTY_AUTH
,
method
:
FORM_DESCRIPTION
.
method
platformName
:
PLATFORM_NAME
});
});
// Spy on AJAX requests
// Initialize the register view
requests
=
AjaxHelpers
.
requests
(
that
);
view
=
new
RegisterView
({
fields
:
FORM_DESCRIPTION
.
fields
,
model
:
model
,
thirdPartyAuth
:
THIRD_PARTY_AUTH
,
platformName
:
PLATFORM_NAME
});
// Spy on AJAX requests
requests
=
AjaxHelpers
.
requests
(
that
);
// Intercept events from the view
// Intercept events from the view
authComplete
=
false
;
authComplete
=
false
;
view
.
on
(
"auth-complete"
,
function
()
{
view
.
on
(
"auth-complete"
,
function
()
{
authComplete
=
true
;
authComplete
=
true
;
});
};
var
submitForm
=
function
(
validationSuccess
)
{
// Simulate manual entry of registration form data
$
(
'#register-email'
).
val
(
USER_DATA
.
email
);
$
(
'#register-name'
).
val
(
USER_DATA
.
name
);
$
(
'#register-username'
).
val
(
USER_DATA
.
username
);
$
(
'#register-password'
).
val
(
USER_DATA
.
password
);
$
(
'#register-level_of_education'
).
val
(
USER_DATA
.
level_of_education
);
$
(
'#register-gender'
).
val
(
USER_DATA
.
gender
);
$
(
'#register-year_of_birth'
).
val
(
USER_DATA
.
year_of_birth
);
$
(
'#register-mailing_address'
).
val
(
USER_DATA
.
mailing_address
);
$
(
'#register-goals'
).
val
(
USER_DATA
.
goals
);
// Check the honor code checkbox
$
(
'#register-honor_code'
).
prop
(
'checked'
,
USER_DATA
.
honor_code
);
// Create a fake click event
var
clickEvent
=
$
.
Event
(
'click'
);
// If validationSuccess isn't passed, we avoid
// spying on `view.validate` twice
if
(
!
_
.
isUndefined
(
validationSuccess
)
)
{
// Force validation to return as expected
spyOn
(
view
,
'validate'
).
andReturn
({
isValid
:
validationSuccess
,
message
:
'Submission was validated.'
});
});
}
;
}
var
submitForm
=
function
(
validationSuccess
)
{
// Submit the email address
// Simulate manual entry of registration form data
view
.
submitForm
(
clickEvent
);
$
(
'#register-email'
).
val
(
USER_DATA
.
email
);
};
$
(
'#register-name'
).
val
(
USER_DATA
.
name
);
$
(
'#register-username'
).
val
(
USER_DATA
.
username
);
$
(
'#register-password'
).
val
(
USER_DATA
.
password
);
$
(
'#register-level_of_education'
).
val
(
USER_DATA
.
level_of_education
);
$
(
'#register-gender'
).
val
(
USER_DATA
.
gender
);
$
(
'#register-year_of_birth'
).
val
(
USER_DATA
.
year_of_birth
);
$
(
'#register-mailing_address'
).
val
(
USER_DATA
.
mailing_address
);
$
(
'#register-goals'
).
val
(
USER_DATA
.
goals
);
// Check the honor code checkbox
$
(
'#register-honor_code'
).
prop
(
'checked'
,
USER_DATA
.
honor_code
);
// Create a fake click event
var
clickEvent
=
$
.
Event
(
'click'
);
// If validationSuccess isn't passed, we avoid
// spying on `view.validate` twice
if
(
!
_
.
isUndefined
(
validationSuccess
)
)
{
// Force validation to return as expected
spyOn
(
view
,
'validate'
).
andReturn
({
isValid
:
validationSuccess
,
message
:
'Submission was validated.'
});
}
// Submit the email address
beforeEach
(
function
()
{
view
.
submitForm
(
clickEvent
);
setFixtures
(
'<div id="register-form"></div>'
);
};
TemplateHelpers
.
installTemplate
(
'templates/student_account/register'
);
TemplateHelpers
.
installTemplate
(
'templates/student_account/form_field'
);
});
beforeEach
(
function
()
{
it
(
'registers a new user'
,
function
()
{
setFixtures
(
'<div id="register-form"></div>'
);
createRegisterView
(
this
);
TemplateHelpers
.
installTemplate
(
'templates/student_account/register'
);
TemplateHelpers
.
installTemplate
(
'templates/student_account/form_field'
);
});
it
(
'registers a new user'
,
function
()
{
// Submit the form, with successful validation
createRegisterView
(
this
);
submitForm
(
true
);
// Submit the form, with successful validation
// Verify that the client contacts the server with the expected data
submitForm
(
true
);
AjaxHelpers
.
expectRequest
(
requests
,
'POST'
,
FORM_DESCRIPTION
.
submit_url
,
$
.
param
(
USER_DATA
)
);
// Verify that the client contacts the server with the expected data
// Respond with status code 200
AjaxHelpers
.
expectRequest
(
AjaxHelpers
.
respondWithJson
(
requests
,
{});
requests
,
'POST'
,
FORM_DESCRIPTION
.
submit_url
,
$
.
param
(
USER_DATA
)
);
// Respond with status code 200
// Verify that auth complete is triggered
AjaxHelpers
.
respondWithJson
(
requests
,
{});
expect
(
authComplete
).
toBe
(
true
);
});
// Verify that auth complete is triggered
it
(
'sends analytics info containing the enrolled course ID'
,
function
()
{
expect
(
authComplete
).
toBe
(
true
);
createRegisterView
(
this
);
// Simulate that the user is attempting to enroll in a course
// by setting the course_id query string param.
spyOn
(
$
,
'url'
).
andCallFake
(
function
(
param
)
{
if
(
param
===
"?course_id"
)
{
return
encodeURIComponent
(
COURSE_ID
);
}
});
});
it
(
'displays third-party auth registration buttons'
,
function
()
{
// Attempt to register
createRegisterView
(
this
);
submitForm
(
true
);
// Verify that Google and Facebook registration buttons are displayed
// Verify that the client sent the course ID for analytics
expect
(
$
(
'.button-Google'
)).
toBeVisible
();
var
expectedData
=
{};
expect
(
$
(
'.button-Facebook'
)).
toBeVisible
();
$
.
extend
(
expectedData
,
USER_DATA
,
{
analytics
:
JSON
.
stringify
({
enroll_course_id
:
COURSE_ID
})
});
});
it
(
'validates registration form fields'
,
function
()
{
AjaxHelpers
.
expectRequest
(
createRegisterView
(
this
);
requests
,
'POST'
,
FORM_DESCRIPTION
.
submit_url
,
$
.
param
(
expectedData
)
);
});
it
(
'displays third-party auth registration buttons'
,
function
()
{
createRegisterView
(
this
);
// Submit the form, with successful validation
// Verify that Google and Facebook registration buttons are displayed
submitForm
(
true
);
expect
(
$
(
'.button-Google'
)).
toBeVisible
();
expect
(
$
(
'.button-Facebook'
)).
toBeVisible
();
});
// Verify that validation of form fields occurred
it
(
'validates registration form fields'
,
function
()
{
expect
(
view
.
validate
).
toHaveBeenCalledWith
(
$
(
'#register-email'
)[
0
]);
createRegisterView
(
this
);
expect
(
view
.
validate
).
toHaveBeenCalledWith
(
$
(
'#register-name'
)[
0
]);
expect
(
view
.
validate
).
toHaveBeenCalledWith
(
$
(
'#register-username'
)[
0
]);
expect
(
view
.
validate
).
toHaveBeenCalledWith
(
$
(
'#register-password'
)[
0
]);
// Verify that no submission errors are visible
// Submit the form, with successful validation
expect
(
view
.
$errors
).
toHaveClass
(
'hidden'
);
submitForm
(
true
);
});
it
(
'displays registration form validation errors'
,
function
()
{
// Verify that validation of form fields occurred
createRegisterView
(
this
);
expect
(
view
.
validate
).
toHaveBeenCalledWith
(
$
(
'#register-email'
)[
0
]);
expect
(
view
.
validate
).
toHaveBeenCalledWith
(
$
(
'#register-name'
)[
0
]);
expect
(
view
.
validate
).
toHaveBeenCalledWith
(
$
(
'#register-username'
)[
0
]);
expect
(
view
.
validate
).
toHaveBeenCalledWith
(
$
(
'#register-password'
)[
0
]);
// Submit the form, with failed validation
// Verify that no submission errors are visible
submitForm
(
false
);
expect
(
view
.
$errors
).
toHaveClass
(
'hidden'
);
});
// Verify that submission errors are visible
it
(
'displays registration form validation errors'
,
function
()
{
expect
(
view
.
$errors
).
not
.
toHaveClass
(
'hidden'
);
createRegisterView
(
this
);
// Expect that auth complete is NOT triggered
// Submit the form, with failed validation
expect
(
authComplete
).
toBe
(
false
);
submitForm
(
false
);
});
// Verify that submission errors are visible
expect
(
view
.
$errors
).
not
.
toHaveClass
(
'hidden'
);
it
(
'displays an error if the server returns an error while registering'
,
function
()
{
// Expect that auth complete is NOT triggered
createRegisterView
(
this
);
expect
(
authComplete
).
toBe
(
false
);
});
// Submit the form, with successful validation
it
(
'displays an error if the server returns an error while registering'
,
function
()
{
submitForm
(
true
);
createRegisterView
(
this
);
// Simulate an error from the LMS servers
// Submit the form, with successful validation
AjaxHelpers
.
respondWithError
(
requests
);
submitForm
(
true
);
// Expect that an error is displayed and that auth complete is NOT triggered
// Simulate an error from the LMS servers
expect
(
view
.
$errors
).
not
.
toHaveClass
(
'hidden'
);
AjaxHelpers
.
respondWithError
(
requests
);
expect
(
authComplete
).
toBe
(
false
);
// If we try again and succeed, the error should go away
// Expect that an error is displayed and that auth complete is NOT triggered
submitForm
();
expect
(
view
.
$errors
).
not
.
toHaveClass
(
'hidden'
);
expect
(
authComplete
).
toBe
(
false
);
// This time, respond with status code 200
// If we try again and succeed, the error should go away
AjaxHelpers
.
respondWithJson
(
requests
,
{}
);
submitForm
(
);
// Expect that the error is hidden and that auth complete is triggered
// This time, respond with status code 200
expect
(
view
.
$errors
).
toHaveClass
(
'hidden'
);
AjaxHelpers
.
respondWithJson
(
requests
,
{});
expect
(
authComplete
).
toBe
(
true
);
});
// Expect that the error is hidden and that auth complete is triggered
expect
(
view
.
$errors
).
toHaveClass
(
'hidden'
);
expect
(
authComplete
).
toBe
(
true
);
});
});
}
}
);
);
}
);
lms/static/js/student_account/models/LoginModel.js
View file @
95a435ab
...
@@ -24,14 +24,27 @@ var edx = edx || {};
...
@@ -24,14 +24,27 @@ var edx = edx || {};
},
},
sync
:
function
(
method
,
model
)
{
sync
:
function
(
method
,
model
)
{
var
headers
=
{
var
headers
=
{
'X-CSRFToken'
:
$
.
cookie
(
'csrftoken'
)
},
'X-CSRFToken'
:
$
.
cookie
(
'csrftoken'
)
data
=
{},
};
analytics
,
courseId
=
$
.
url
(
'?course_id'
);
// If there is a course ID in the query string param,
// send that to the server as well so it can be included
// in analytics events.
if
(
courseId
)
{
analytics
=
JSON
.
stringify
({
enroll_course_id
:
decodeURIComponent
(
courseId
)
});
}
// Include all form fields and analytics info in the data sent to the server
$
.
extend
(
data
,
model
.
attributes
,
{
analytics
:
analytics
});
$
.
ajax
({
$
.
ajax
({
url
:
model
.
urlRoot
,
url
:
model
.
urlRoot
,
type
:
model
.
ajaxType
,
type
:
model
.
ajaxType
,
data
:
model
.
attributes
,
data
:
data
,
headers
:
headers
,
headers
:
headers
,
success
:
function
()
{
success
:
function
()
{
model
.
trigger
(
'sync'
);
model
.
trigger
(
'sync'
);
...
...
lms/static/js/student_account/models/RegisterModel.js
View file @
95a435ab
...
@@ -30,14 +30,27 @@ var edx = edx || {};
...
@@ -30,14 +30,27 @@ var edx = edx || {};
},
},
sync
:
function
(
method
,
model
)
{
sync
:
function
(
method
,
model
)
{
var
headers
=
{
var
headers
=
{
'X-CSRFToken'
:
$
.
cookie
(
'csrftoken'
)
},
'X-CSRFToken'
:
$
.
cookie
(
'csrftoken'
)
data
=
{},
};
analytics
,
courseId
=
$
.
url
(
'?course_id'
);
// If there is a course ID in the query string param,
// send that to the server as well so it can be included
// in analytics events.
if
(
courseId
)
{
analytics
=
JSON
.
stringify
({
enroll_course_id
:
decodeURIComponent
(
courseId
)
});
}
// Include all form fields and analytics info in the data sent to the server
$
.
extend
(
data
,
model
.
attributes
,
{
analytics
:
analytics
});
$
.
ajax
({
$
.
ajax
({
url
:
model
.
urlRoot
,
url
:
model
.
urlRoot
,
type
:
model
.
ajaxType
,
type
:
model
.
ajaxType
,
data
:
model
.
attributes
,
data
:
data
,
headers
:
headers
,
headers
:
headers
,
success
:
function
()
{
success
:
function
()
{
model
.
trigger
(
'sync'
);
model
.
trigger
(
'sync'
);
...
...
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