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
753d7821
Commit
753d7821
authored
Oct 28, 2016
by
muhammad-ammar
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
single search request to fetch notes for HTML components per unit
TNL-4163
parent
1c5d1f1c
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
244 additions
and
62 deletions
+244
-62
lms/static/js/edxnotes/plugins/search_override.js
+13
-0
lms/static/js/edxnotes/utils/notes_collector.js
+113
-0
lms/static/js/edxnotes/views/notes_factory.js
+9
-3
lms/static/js/spec/edxnotes/plugins/store_error_handler_spec.js
+16
-11
lms/static/js/spec/edxnotes/utils/notes_collector_spec.js
+42
-0
lms/static/js/spec/edxnotes/views/notes_factory_spec.js
+21
-15
lms/static/js/spec/edxnotes/views/notes_visibility_factory_spec.js
+29
-33
lms/static/lms/js/spec/main.js
+1
-0
No files found.
lms/static/js/edxnotes/plugins/search_override.js
0 → 100644
View file @
753d7821
(
function
(
define
)
{
'use strict'
;
define
([
'annotator_1.2.9'
],
function
(
Annotator
)
{
//
// Override Annotator.Plugin.Store.prototype._getAnnotations. We don't want AnnotatorJS to search notes.
//
// eslint-disable-next-line no-param-reassign, no-underscore-dangle
Annotator
.
Plugin
.
Store
.
prototype
.
_getAnnotations
=
function
()
{
// Do Nothing
};
});
}).
call
(
this
,
define
||
RequireJS
.
define
);
lms/static/js/edxnotes/utils/notes_collector.js
0 → 100644
View file @
753d7821
(
function
(
define
)
{
'use strict'
;
define
([
'jquery'
,
'underscore'
,
'annotator_1.2.9'
],
function
(
$
,
_
,
Annotator
)
{
var
cleanup
,
renderNotes
,
fetchNotesWhenReady
,
storeNotesRequestData
,
searchRequestsData
=
[];
/**
* Clears the searchRequestsData.
*/
cleanup
=
function
()
{
searchRequestsData
=
[];
};
/**
* Store requests data for each annotatable component and fetch
* notes for them when request for each component is stored.
*
* @param {object} data Request data for each annotatable component
* @param {Number} totalNotesWrappers Total number of edx notes wrappers present
*/
storeNotesRequestData
=
function
(
data
,
totalNotesWrappers
)
{
searchRequestsData
.
push
(
data
);
fetchNotesWhenReady
(
totalNotesWrappers
);
};
/**
* Fetch notes for annotatable components only when desired
* number of requests are stored.
*
* @param {Number} totalNotesWrappers Total number of edx notes wrappers present
*/
fetchNotesWhenReady
=
function
(
totalNotesWrappers
)
{
var
settings
,
usageIds
,
searchEndpoint
;
if
(
totalNotesWrappers
!==
searchRequestsData
.
length
)
{
return
;
}
// `user` and `course_id` values are same for every annotatable
// component so we pick these from first `searchRequestsData` item
settings
=
{
data
:
{
user
:
searchRequestsData
[
0
].
params
.
user
,
course_id
:
searchRequestsData
[
0
].
params
.
courseId
},
type
:
'GET'
,
dataType
:
'json'
,
headers
:
{
'x-annotator-auth-token'
:
searchRequestsData
[
0
].
params
.
token
}
};
searchEndpoint
=
searchRequestsData
[
0
].
params
.
endpoint
+
'search/?'
;
usageIds
=
_
.
map
(
searchRequestsData
,
function
(
item
)
{
return
'usage_id='
+
encodeURIComponent
(
item
.
params
.
usageId
);
});
// Search endpoint expects the below format for query params
// /api/v1/search/?course_id={course_id}&user={user_id}&usage_id={usage_id}&usage_id={usage_id} ...
searchEndpoint
+=
usageIds
.
join
(
'&'
);
$
.
ajax
(
searchEndpoint
,
settings
)
.
done
(
function
(
jqXHR
)
{
renderNotes
(
jqXHR
);
})
.
fail
(
function
(
jqXHR
)
{
// `_action` is used by AnnotatorJS to construct error message
jqXHR
.
_action
=
'search'
;
// eslint-disable-line no-underscore-dangle, no-param-reassign
Annotator
.
Plugin
.
Store
.
prototype
.
_onError
(
jqXHR
);
// eslint-disable-line no-underscore-dangle
})
.
always
(
function
()
{
cleanup
();
});
};
/**
* Pass notes to AnnotatorJS for rendering
*
* @param {Array} notes Notes data received from server
*/
renderNotes
=
function
(
notes
)
{
var
edxNotes
=
{};
// AnnotatorJS expects notes to be present in an array named as `rows`
_
.
each
(
searchRequestsData
,
function
(
item
)
{
edxNotes
[
item
.
params
.
usageId
]
=
{
rows
:
[]};
});
// Place the received notes in the format below
// edxNotes = {
// 'usage_id1': [noteObject, noteObject, noteObject],
// 'usage_id2': [noteObject, noteObject]
// }
_
.
each
(
notes
,
function
(
note
)
{
edxNotes
[
note
.
usage_id
].
rows
.
push
(
note
);
});
// Render the notes for each annotatable component using its associated AnnotatorJS instance
_
.
each
(
searchRequestsData
,
function
(
item
)
{
item
.
annotator
.
plugins
.
Store
.
_onLoadAnnotationsFromSearch
(
// eslint-disable-line no-underscore-dangle
edxNotes
[
item
.
params
.
usageId
]
);
});
};
return
{
storeNotesRequestData
:
storeNotesRequestData
,
cleanup
:
cleanup
};
});
}).
call
(
this
,
define
||
RequireJS
.
define
);
lms/static/js/edxnotes/views/notes_factory.js
View file @
753d7821
(
function
(
define
,
undefined
)
{
'use strict'
;
define
([
'jquery'
,
'underscore'
,
'annotator_1.2.9'
,
'js/edxnotes/utils/logger'
,
'jquery'
,
'underscore'
,
'annotator_1.2.9'
,
'js/edxnotes/utils/logger'
,
'js/edxnotes/utils/notes_collector'
,
'js/edxnotes/views/shim'
,
'js/edxnotes/plugins/scroller'
,
'js/edxnotes/plugins/events'
,
'js/edxnotes/plugins/accessibility'
,
'js/edxnotes/plugins/caret_navigation'
,
'js/edxnotes/plugins/store_error_handler'
],
function
(
$
,
_
,
Annotator
,
NotesLogger
)
{
'js/edxnotes/plugins/store_error_handler'
,
'js/edxnotes/plugins/search_override'
],
function
(
$
,
_
,
Annotator
,
NotesLogger
,
NotesCollector
)
{
var
plugins
=
[
'Auth'
,
'Store'
,
'Scroller'
,
'Events'
,
'Accessibility'
,
'CaretNavigation'
,
'Tags'
],
getOptions
,
setupPlugins
,
getAnnotator
;
...
...
@@ -84,6 +86,10 @@
annotator
=
el
.
annotator
(
options
).
data
(
'annotator'
);
setupPlugins
(
annotator
,
plugins
,
options
);
NotesCollector
.
storeNotesRequestData
(
{
annotator
:
annotator
,
params
:
params
},
$
(
'.edx-notes-wrapper'
).
length
);
annotator
.
logger
=
logger
;
logger
.
log
({
'element'
:
element
,
'options'
:
options
});
return
annotator
;
...
...
lms/static/js/spec/edxnotes/plugins/store_error_handler_spec.js
View file @
753d7821
...
...
@@ -2,30 +2,35 @@ define([
'jquery'
,
'underscore'
,
'annotator_1.2.9'
,
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers'
,
'js/spec/edxnotes/helpers'
,
'js/edxnotes/views/notes_factory'
],
function
(
$
,
_
,
Annotator
,
AjaxHelpers
,
Helpers
,
NotesFactory
)
{
'js/edxnotes/views/notes_factory'
,
'js/edxnotes/utils/notes_collector'
],
function
(
$
,
_
,
Annotator
,
AjaxHelpers
,
Helpers
,
NotesFactory
,
NotesCollector
)
{
'use strict'
;
describe
(
'Store Error Handler Custom Message'
,
function
()
{
beforeEach
(
function
()
{
spyOn
(
Annotator
,
'showNotification'
);
loadFixtures
(
'js/fixtures/edxnotes/edxnotes_wrapper.html'
);
this
.
wrapper
=
document
.
getElementById
(
'edx-notes-wrapper-123'
);
NotesCollector
.
cleanup
(
);
});
afterEach
(
function
()
{
_
.
invoke
(
Annotator
.
_instances
,
'destroy'
);
while
(
Annotator
.
_instances
.
length
>
0
)
{
// eslint-disable-line no-underscore-dangle
Annotator
.
_instances
[
0
].
destroy
();
// eslint-disable-line no-underscore-dangle
}
});
it
(
'can handle custom error if sent from server'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
var
token
=
Helpers
.
makeToken
();
NotesFactory
.
factory
(
this
.
wrapper
,
{
endpoint
:
'/test_endpoint'
,
user
:
'a user'
,
usageId
:
'an usage'
,
courseId
:
'a course'
,
token
:
token
,
tokenUrl
:
'/test_token_url'
_
.
each
(
$
(
'.edx-notes-wrapper'
),
function
(
wrapper
)
{
NotesFactory
.
factory
(
wrapper
,
{
endpoint
:
'/test_endpoint'
,
user
:
'a user'
,
usageId
:
'an usage'
,
courseId
:
'a course'
,
token
:
token
,
tokenUrl
:
'/test_token_url'
});
});
var
errorMsg
=
'can
\'
t create more notes'
;
...
...
lms/static/js/spec/edxnotes/utils/notes_collector_spec.js
0 → 100644
View file @
753d7821
define
([
'jquery'
,
'underscore'
,
'annotator_1.2.9'
,
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers'
,
'js/edxnotes/views/notes_factory'
,
'js/edxnotes/utils/notes_collector'
,
'js/spec/edxnotes/helpers'
],
function
(
$
,
_
,
Annotator
,
AjaxHelpers
,
NotesFactory
,
NotesCollector
,
Helpers
)
{
'use strict'
;
describe
(
'EdxNotes NotesCollector'
,
function
()
{
beforeEach
(
function
()
{
loadFixtures
(
'js/fixtures/edxnotes/edxnotes_wrapper.html'
);
NotesCollector
.
cleanup
();
});
afterEach
(
function
()
{
while
(
Annotator
.
_instances
.
length
>
0
)
{
// eslint-disable-line no-underscore-dangle
Annotator
.
_instances
[
0
].
destroy
();
// eslint-disable-line no-underscore-dangle
}
NotesCollector
.
cleanup
();
});
it
(
'sends single search request to fetch notes for all HTML components'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
),
token
=
Helpers
.
makeToken
();
_
.
each
(
$
(
'.edx-notes-wrapper'
),
function
(
wrapper
,
index
)
{
NotesFactory
.
factory
(
wrapper
,
{
endpoint
:
'/test_endpoint/'
,
user
:
'a user'
,
usageId
:
'usage '
+
index
,
courseId
:
'a course'
,
token
:
token
,
tokenUrl
:
'/test_token_url'
});
});
expect
(
requests
.
length
).
toBe
(
1
);
AjaxHelpers
.
expectJsonRequest
(
requests
,
'GET'
,
'/test_endpoint/search/?usage_id=usage%200&usage_id=usage%201&user=a+user&course_id=a+course'
);
});
});
});
lms/static/js/spec/edxnotes/views/notes_factory_spec.js
View file @
753d7821
define
([
'
annotator_1.2.9'
,
'js/edxnotes/views/notes_factory'
,
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
'
,
'js/spec/edxnotes/helpers'
],
function
(
Annotator
,
NotesFactory
,
AjaxHelpers
,
Helpers
)
{
'
jquery'
,
'underscore'
,
'annotator_1.2.9'
,
'js/edxnotes/views/notes_factory
'
,
'
edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers'
,
'js/edxnotes/utils/notes_collector'
,
'
js/spec/edxnotes/helpers'
],
function
(
$
,
_
,
Annotator
,
NotesFactory
,
AjaxHelpers
,
NotesCollector
,
Helpers
)
{
'use strict'
;
describe
(
'EdxNotes NotesFactory'
,
function
()
{
beforeEach
(
function
()
{
loadFixtures
(
'js/fixtures/edxnotes/edxnotes_wrapper.html'
);
this
.
wrapper
=
document
.
getElementById
(
'edx-notes-wrapper-123'
);
NotesCollector
.
cleanup
(
);
});
afterEach
(
function
()
{
...
...
@@ -15,30 +15,36 @@ define([
}
});
it
(
'can initialize annotator correctly'
,
function
()
{
it
(
'can initialize annotator correctly'
,
function
(
done
)
{
var
requests
=
AjaxHelpers
.
requests
(
this
),
token
=
Helpers
.
makeToken
(),
options
=
{
user
:
'a user'
,
usage_id
:
'an usage'
,
course_id
:
'a course'
},
annotator
=
NotesFactory
.
factory
(
this
.
wrapper
,
{
};
_
.
each
(
$
(
'.edx-notes-wrapper'
),
function
(
wrapper
)
{
var
annotator
=
NotesFactory
.
factory
(
wrapper
,
{
endpoint
:
'/test_endpoint'
,
user
:
'a user'
,
usageId
:
'an usage'
,
courseId
:
'a course'
,
token
:
token
,
tokenUrl
:
'/test_token_url'
}),
request
=
requests
[
0
];
});
expect
(
requests
).
toHaveLength
(
1
);
expect
(
request
.
requestHeaders
[
'x-annotator-auth-token'
]).
toBe
(
token
);
expect
(
annotator
.
options
.
auth
.
tokenUrl
).
toBe
(
'/test_token_url'
);
expect
(
annotator
.
options
.
store
.
prefix
).
toBe
(
'/test_endpoint'
);
expect
(
annotator
.
options
.
store
.
annotationData
).
toEqual
(
options
);
expect
(
annotator
.
options
.
store
.
loadFromSearch
).
toEqual
(
options
);
expect
(
annotator
.
options
.
auth
.
tokenUrl
).
toBe
(
'/test_token_url'
);
expect
(
annotator
.
options
.
store
.
prefix
).
toBe
(
'/test_endpoint'
);
expect
(
annotator
.
options
.
store
.
annotationData
).
toEqual
(
options
);
expect
(
annotator
.
options
.
store
.
loadFromSearch
).
toEqual
(
options
);
});
jasmine
.
waitUntil
(
function
()
{
return
requests
.
length
===
1
;
}).
done
(
function
()
{
expect
(
requests
[
0
].
requestHeaders
[
'x-annotator-auth-token'
]).
toBe
(
token
);
done
();
});
});
});
});
lms/static/js/spec/edxnotes/views/notes_visibility_factory_spec.js
View file @
753d7821
define
([
'jquery'
,
'underscore'
,
'annotator_1.2.9'
,
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers'
,
'js/edxnotes/views/notes_visibility_factory'
,
'js/spec/edxnotes/helpers'
'js/edxnotes/views/notes_visibility_factory'
,
'js/
edxnotes/utils/notes_collector'
,
'js/
spec/edxnotes/helpers'
],
function
(
$
,
_
,
Annotator
,
AjaxHelpers
,
NotesVisibilityFactory
,
Helpers
$
,
_
,
Annotator
,
AjaxHelpers
,
NotesVisibilityFactory
,
NotesCollector
,
Helpers
)
{
'use strict'
;
describe
(
'EdxNotes ToggleNotesFactory'
,
function
()
{
var
params
=
{
endpoint
:
'/test_endpoint'
,
user
:
'
a user
'
,
usageId
:
'
an usage
'
,
courseId
:
'
a course
'
,
endpoint
:
'/test_endpoint
/
'
,
user
:
'
user12345
'
,
usageId
:
'
usageid777
'
,
courseId
:
'
courseid000
'
,
token
:
Helpers
.
makeToken
(),
tokenUrl
:
'/test_token_url'
};
...
...
@@ -27,10 +27,11 @@ define([
document
.
getElementById
(
'edx-notes-wrapper-456'
),
params
,
true
);
this
.
toggleNotes
=
NotesVisibilityFactory
.
ToggleVisibilityView
(
true
,
'/test_url'
);
this
.
b
utton
=
$
(
'.action-toggle-notes'
);
this
.
label
=
this
.
b
utton
.
find
(
'.utility-control-label'
);
this
.
toggleVisibilityB
utton
=
$
(
'.action-toggle-notes'
);
this
.
label
=
this
.
toggleVisibilityB
utton
.
find
(
'.utility-control-label'
);
this
.
toggleMessage
=
$
(
'.action-toggle-message'
);
spyOn
(
this
.
toggleNotes
,
'toggleHandler'
).
and
.
callThrough
();
NotesCollector
.
cleanup
();
});
afterEach
(
function
()
{
...
...
@@ -39,49 +40,45 @@ define([
Annotator
.
_instances
[
0
].
destroy
();
}
$
(
'.annotator-notice'
).
remove
();
NotesCollector
.
cleanup
();
});
it
(
'can toggle notes'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
expect
(
this
.
b
utton
).
not
.
toHaveClass
(
'is-disabled'
);
expect
(
this
.
toggleVisibilityB
utton
).
not
.
toHaveClass
(
'is-disabled'
);
expect
(
this
.
label
).
toContainText
(
'Hide notes'
);
expect
(
this
.
b
utton
).
toHaveClass
(
'is-active'
);
expect
(
this
.
b
utton
).
toHaveAttr
(
'aria-pressed'
,
'true'
);
expect
(
this
.
toggleVisibilityB
utton
).
toHaveClass
(
'is-active'
);
expect
(
this
.
toggleVisibilityB
utton
).
toHaveAttr
(
'aria-pressed'
,
'true'
);
expect
(
this
.
toggleMessage
).
not
.
toHaveClass
(
'is-fleeting'
);
expect
(
this
.
toggleMessage
).
toContainText
(
'Notes visible'
);
this
.
b
utton
.
click
();
this
.
toggleVisibilityB
utton
.
click
();
expect
(
this
.
label
).
toContainText
(
'Show notes'
);
expect
(
this
.
b
utton
).
not
.
toHaveClass
(
'is-active'
);
expect
(
this
.
toggleVisibilityB
utton
).
not
.
toHaveClass
(
'is-active'
);
expect
(
this
.
toggleMessage
).
toHaveClass
(
'is-fleeting'
);
expect
(
this
.
toggleMessage
).
toContainText
(
'Notes hidden'
);
expect
(
Annotator
.
_instances
).
toHaveLength
(
0
);
AjaxHelpers
.
expectJsonRequest
(
requests
,
'PUT'
,
'/test_url'
,
{
'visibility'
:
false
visibility
:
false
});
AjaxHelpers
.
respondWithJson
(
requests
,
{});
this
.
b
utton
.
click
();
this
.
toggleVisibilityB
utton
.
click
();
expect
(
this
.
label
).
toContainText
(
'Hide notes'
);
expect
(
this
.
b
utton
).
toHaveClass
(
'is-active'
);
expect
(
this
.
toggleVisibilityB
utton
).
toHaveClass
(
'is-active'
);
expect
(
this
.
toggleMessage
).
toHaveClass
(
'is-fleeting'
);
expect
(
this
.
toggleMessage
).
toContainText
(
'Notes visible'
);
expect
(
Annotator
.
_instances
).
toHaveLength
(
2
);
// TODO: why is the same search request made twice?
AjaxHelpers
.
expectJsonRequest
(
requests
,
'GET'
,
'/test_endpoint/search/?us
er=a+user&usage_id=an+usage&course_id=a+course
'
'/test_endpoint/search/?us
age_id=usageid777&usage_id=usageid777&user=user12345&course_id=courseid000
'
);
AjaxHelpers
.
respondWithJson
(
requests
,
{});
AjaxHelpers
.
expectJsonRequest
(
requests
,
'GET'
,
'/test_endpoint/search/?user=a+user&usage_id=an+usage&course_id=a+course'
);
AjaxHelpers
.
respondWithJson
(
requests
,
{});
AjaxHelpers
.
respondWithJson
(
requests
,
[]);
AjaxHelpers
.
expectJsonRequest
(
requests
,
'PUT'
,
'/test_url'
,
{
'visibility'
:
true
visibility
:
true
});
AjaxHelpers
.
respondWithJson
(
requests
,
{});
});
...
...
@@ -90,7 +87,7 @@ define([
var
requests
=
AjaxHelpers
.
requests
(
this
),
$errorContainer
=
$
(
'.annotator-notice'
);
this
.
b
utton
.
click
();
this
.
toggleVisibilityB
utton
.
click
();
AjaxHelpers
.
respondWithError
(
requests
);
expect
(
$errorContainer
).
toContainText
(
'An error has occurred. Make sure that you are connected to the Internet, '
+
...
...
@@ -100,19 +97,18 @@ define([
expect
(
$errorContainer
).
toHaveClass
(
'annotator-notice-show'
);
expect
(
$errorContainer
).
toHaveClass
(
'annotator-notice-error'
);
this
.
b
utton
.
click
();
this
.
toggleVisibilityB
utton
.
click
();
// TODO: why is the same search request made twice?
AjaxHelpers
.
expectJsonRequest
(
requests
,
'GET'
,
'/test_endpoint/search/?us
er=a+user&usage_id=an+usage&course_id=a+course
'
'/test_endpoint/search/?us
age_id=usageid777&usage_id=usageid777&user=user12345&course_id=courseid000
'
);
AjaxHelpers
.
respondWithJson
(
requests
,
{});
AjaxHelpers
.
expectJsonRequest
(
requests
,
'GET'
,
'/test_endpoint/search/?user=a+user&usage_id=an+usage&course_id=a+course'
);
AjaxHelpers
.
respondWithJson
(
requests
,
{});
AjaxHelpers
.
respondWithJson
(
requests
,
[]);
AjaxHelpers
.
expectJsonRequest
(
requests
,
'PUT'
,
'/test_url'
,
{
visibility
:
true
});
AjaxHelpers
.
respondWithJson
(
requests
,
{});
expect
(
$errorContainer
).
not
.
toHaveClass
(
'annotator-notice-show'
);
});
...
...
lms/static/lms/js/spec/main.js
View file @
753d7821
...
...
@@ -701,6 +701,7 @@
'js/spec/edxnotes/plugins/scroller_spec.js'
,
'js/spec/edxnotes/plugins/store_error_handler_spec.js'
,
'js/spec/edxnotes/utils/logger_spec.js'
,
'js/spec/edxnotes/utils/notes_collector_spec.js'
,
'js/spec/edxnotes/views/note_item_spec.js'
,
'js/spec/edxnotes/views/notes_factory_spec.js'
,
'js/spec/edxnotes/views/notes_page_spec.js'
,
...
...
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