Add search to RCE's media tray

builds on LS-1235 and adds search to the media panel in the canvas
content tray

closes LS-1518
flag=rce_enhancements

test plan:
  - Having g/250818 and g/250994 included in your RCS is a prereq.
  - in the RCE open Media > Course|User|Group Media
  > expect all the docs in the tray
  - start typing in the search text input
  - type 2 characters and stop
  > expect nothing to happen
  > expect the tray to reload once you've typed at least 3 chars
  > expect the results to match the search string
  - clear the search text input
  > expect the tray to reload with all the documents

  - perform this on videos with their original title (which would be
    the file name), and with a video you've renamed in the Video Options
    tray. The search should locate the media based on your updated
    title

  - the Search box should not appear for Links

Change-Id: I10cd5007ad950f5c946bcc3cb0abb23326ca4ca6
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/250993
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
Reviewed-by: Jackson Howe <jackson.howe@instructure.com>
QA-Review: Robin Kuss <rkuss@instructure.com>
Product-Review: Peyton Craighill <pcraighill@instructure.com>
This commit is contained in:
Ed Schiebel 2020-10-23 11:13:16 -04:00
parent 019d963d65
commit 4aa5d67d3a
13 changed files with 537 additions and 376 deletions

View File

@ -149,6 +149,7 @@ class MediaObjectsController < ApplicationController
order_by = MediaObject.best_unicode_collation_key('COALESCE(user_entered_title, title)')
end
scope = scope.order(order_by => order_dir)
scope = MediaObject.search_by_attribute(scope, :title, params[:search_term])
exclude = params[:exclude] || []
media_objects =

View File

@ -22,6 +22,7 @@ require 'csv'
class MediaObject < ActiveRecord::Base
include Workflow
include SearchTermHelper
belongs_to :user
belongs_to :context, polymorphic:
[:course, :group, :conversation_message, :account, :assignment,

View File

@ -35,7 +35,8 @@ function getPanelProps(contextType, mediaprops) {
...mediaprops
}
},
sortBy: {sort: 'alphabetical', order: 'asc'}
sortBy: {sort: 'alphabetical', order: 'asc'},
searchString: 'whereami'
}
}

View File

@ -58,7 +58,7 @@ function renderLoadingError(_error) {
}
export default function MediaPanel(props) {
const {fetchInitialMedia, fetchNextMedia, contextType, sortBy} = props
const {fetchInitialMedia, fetchNextMedia, contextType, sortBy, searchString} = props
const media = props.media[contextType]
const {hasMore, isLoading, error, files} = media
const lastItemRef = useRef(null)
@ -71,7 +71,8 @@ export default function MediaPanel(props) {
onLoadMore: fetchNextMedia,
records: files,
contextType,
sortBy
sortBy,
searchString
})
const handleFileClick = file => {
@ -116,5 +117,6 @@ MediaPanel.propTypes = {
sortBy: shape({
sort: oneOf(['date_added', 'alphabetical']).isRequired,
order: oneOf(['asc', 'desc']).isRequired
})
}),
searchString: string
}

View File

@ -207,7 +207,7 @@ export default function Filter(props) {
</Flex.Item>
)}
</Flex>
{['images', 'documents'].indexOf(contentSubtype) >= 0 && (
{['images', 'documents', 'media'].indexOf(contentSubtype) >= 0 && (
<View as="div" margin="small none none none">
<TextInput
renderLabel={<ScreenReaderContent>Search</ScreenReaderContent>}

View File

@ -317,13 +317,13 @@ describe('RCE Plugins > Filter', () => {
expect(component.getByPlaceholderText('Search')).toBeInTheDocument()
})
it('is not visible when the contentSubtype is media', () => {
it('is visible when the contentSubtype is media', () => {
renderComponent({
userContextType: 'course',
contentType: 'course_files',
contentSubtype: 'media'
})
expect(component.queryByPlaceholderText('Search')).toBe(null)
expect(component.queryByPlaceholderText('Search')).toBeInTheDocument()
})
it('is not visible when the contentType is links', () => {

View File

@ -44,12 +44,12 @@ export function failMedia({error, contextType}) {
// dispatches the start of the load, requests a page for the collection from
// the source, then dispatches the loaded page to the store on success or
// clears the load on failure
export function fetchMedia(sortBy) {
export function fetchMedia(sortBy, searchString) {
return (dispatch, getState) => {
const state = getState()
dispatch(requestMedia(state.contextType))
return state.source
.fetchMedia({...state, ...sortBy})
.fetchMedia({...state, ...sortBy, searchString})
.then(response => dispatch(receiveMedia({response, contextType: state.contextType})))
.catch(error => dispatch(failMedia({error, contextType: state.contextType})))
}
@ -57,26 +57,26 @@ export function fetchMedia(sortBy) {
// fetches a page only if a page is not already being loaded and the
// collection is not yet completely loaded
export function fetchNextMedia(sortBy) {
export function fetchNextMedia(sortBy, searchString) {
return (dispatch, getState) => {
const state = getState()
const media = state.media[state.contextType]
if (!media?.isLoading && media?.hasMore) {
dispatch(requestMedia(state.contextType))
return dispatch(fetchMedia(sortBy))
return dispatch(fetchMedia(sortBy, searchString))
}
}
}
// fetches the next page (subject to conditions on fetchNextMedia) only if the
// collection is currently empty
export function fetchInitialMedia(sortBy) {
export function fetchInitialMedia(sortBy, searchString) {
return (dispatch, getState) => {
const state = getState()
dispatch(requestInitialMedia(state.contextType))
return dispatch(fetchMedia(sortBy))
return dispatch(fetchMedia(sortBy, searchString))
}
}

View File

@ -61,8 +61,8 @@ export default function propsFromDispatch(dispatch) {
mediaUploadComplete: (error, uploadData) => dispatch(mediaUploadComplete(error, uploadData)),
fetchInitialDocs: (sortBy, searchString) => dispatch(fetchInitialDocs(sortBy, searchString)),
fetchNextDocs: (sortBy, searchString) => dispatch(fetchNextDocs(sortBy, searchString)),
fetchInitialMedia: sortBy => dispatch(fetchInitialMedia(sortBy)),
fetchNextMedia: sortBy => dispatch(fetchNextMedia(sortBy)),
fetchInitialMedia: (sortBy, searchString) => dispatch(fetchInitialMedia(sortBy, searchString)),
fetchNextMedia: (sortBy, searchString) => dispatch(fetchNextMedia(sortBy, searchString)),
updateMediaObject: new_values => dispatch(updateMediaObject(new_values)),
onChangeContext: newContext => dispatch(changeContext(newContext))
}

View File

@ -547,7 +547,10 @@ class RceApiSource {
)}${getSearchParam(props.searchString)}`
break
case 'media_objects': // when requesting media objects (this is the currently used branch)
extra = getSortParams(props.sort === 'alphabetical' ? 'title' : 'date', props.order)
extra = `${getSortParams(
props.sort === 'alphabetical' ? 'title' : 'date',
props.order
)}${getSearchParam(props.searchString)}`
break
}
return `${this.baseUri(

View File

@ -27,6 +27,7 @@ import {
import alertHandler from '../../../src/rce/alertHandler'
const sortBy = {sort: 'alphabetical', order: 'asc'}
const searchString = 'hello'
function getInitialState() {
return {
@ -58,13 +59,13 @@ describe('Media actions', () => {
it('fetches initial page', () => {
const dispatchSpy = sinon.spy()
const getState = getInitialState
actions.fetchInitialMedia(sortBy)(dispatchSpy, getState)
actions.fetchInitialMedia(sortBy, searchString)(dispatchSpy, getState)
assert(dispatchSpy.called)
})
it('fetches next page if necessary', () => {
const dispatchSpy = sinon.spy()
const getState = getInitialState
actions.fetchNextMedia(sortBy)(dispatchSpy, getState)
actions.fetchNextMedia(sortBy, searchString)(dispatchSpy, getState)
assert(dispatchSpy.called)
})
it('always fetches initial fetch page', () => {
@ -76,7 +77,7 @@ describe('Media actions', () => {
state.media.course.files = [{one: '1'}, {two: '2'}, {three: '3'}]
return state
}
actions.fetchInitialMedia(sortBy)(dispatchSpy, getState)
actions.fetchInitialMedia(sortBy, searchString)(dispatchSpy, getState)
assert(dispatchSpy.called)
})
it('fetches if there is more to load', () => {
@ -87,7 +88,7 @@ describe('Media actions', () => {
state.media.course.hasMore = true
return state
}
actions.fetchNextMedia(sortBy)(dispatchSpy, getState)
actions.fetchNextMedia(sortBy, searchString)(dispatchSpy, getState)
assert(dispatchSpy.called)
})
it('does not fetch if requested but no more to load', () => {
@ -98,7 +99,7 @@ describe('Media actions', () => {
state.media.course.hasMore = false
return state
}
actions.fetchNextMedia(sortBy)(dispatchSpy, getState)
actions.fetchNextMedia(sortBy, searchString)(dispatchSpy, getState)
assert(!dispatchSpy.called)
})
it('fetches media', async () => {
@ -111,7 +112,7 @@ describe('Media actions', () => {
}
return state
}
await actions.fetchMedia(sortBy)(dispatchSpy, getState)
await actions.fetchMedia(sortBy, searchString)(dispatchSpy, getState)
assert(
dispatchSpy.calledWith({type: actions.REQUEST_MEDIA, payload: {contextType: 'course'}})
)

View File

@ -149,17 +149,29 @@ describe('sidebarHandlers', () => {
})
it('ties media fetch initial media to store', () => {
testHandler('fetchInitialMedia', media, 'fetchInitialMedia', {
sort: 'alphabetical',
order: 'asc'
})
testHandler(
'fetchInitialMedia',
media,
'fetchInitialMedia',
{
sort: 'alphabetical',
order: 'asc'
},
'search-string'
)
})
it('ties media fetch next media to store', () => {
testHandler('fetchNextMedia', media, 'fetchNextMedia', {
sort: 'alphabetical',
order: 'asc'
})
testHandler(
'fetchNextMedia',
media,
'fetchNextMedia',
{
sort: 'alphabetical',
order: 'asc'
},
'search-string'
)
})
it('ties media update media object to store', () => {

View File

@ -150,11 +150,11 @@ describe('sources/api', () => {
)
})
it('gets media', () => {
it('gets media_objects', () => {
const uri = apiSource.uriFor('media_objects', props)
assert.strictEqual(
uri,
'/api/media_objects?contextType=course&contextId=17&sort=title&order=asc'
'/api/media_objects?contextType=course&contextId=17&sort=title&order=asc&search_term=hello%20world'
)
})
})

View File

@ -20,512 +20,652 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe MediaObjectsController do
describe "GET 'show'" do
before do
# We don't actually want to ping kaltura during these tests
allow(MediaObject).to receive(:media_id_exists?).and_return(true)
allow_any_instance_of(MediaObject).to receive(:media_sources).and_return([{:url => "whatever man", :bitrate => 12345}])
allow_any_instance_of(MediaObject).to receive(:media_sources).and_return(
[{ url: 'whatever man', bitrate: 12_345 }]
)
end
it "should create a MediaObject if necessary on request" do
it 'should create a MediaObject if necessary on request' do
# this test is purposely run with no user logged in to make sure it works in public courses
missing_media_id = "0_12345678"
missing_media_id = '0_12345678'
expect(MediaObject.by_media_id(missing_media_id)).to be_empty
get 'show', params: {:media_object_id => missing_media_id}
expect(json_parse(response.body)).to include({
'can_add_captions' => false,
'media_id' => missing_media_id,
'title' => "Untitled",
'media_type' => nil,
'media_tracks' => [],
'media_sources' => [{"bitrate"=>12345, "label"=>"12 kbps", "src"=>"whatever man", "url"=>"whatever man"}]
})
get 'show', params: { media_object_id: missing_media_id }
expect(json_parse(response.body)).to include(
{
'can_add_captions' => false,
'media_id' => missing_media_id,
'title' => 'Untitled',
'media_type' => nil,
'media_tracks' => [],
'media_sources' => [
{
'bitrate' => 12_345,
'label' => '12 kbps',
'src' => 'whatever man',
'url' => 'whatever man'
}
]
}
)
expect(MediaObject.by_media_id(missing_media_id).first.media_id).to eq missing_media_id
end
it "should retrieve info about a 'deleted' MediaObject" do
deleted_media_id = '0_deadbeef'
course_factory
mo = media_object = course_factory.media_objects.build :media_id => deleted_media_id
mo = media_object = course_factory.media_objects.build media_id: deleted_media_id
media_object.workflow_state = 'deleted'
media_object.save!
get 'show', params: {:media_object_id => deleted_media_id}
expect(json_parse(response.body)).to eq({
get 'show', params: { media_object_id: deleted_media_id }
expect(json_parse(response.body)).to eq(
{
'can_add_captions' => false,
'created_at' => mo.created_at.as_json,
'media_id' => deleted_media_id,
'title' => "Untitled",
'title' => 'Untitled',
'media_type' => nil,
'media_tracks' => [],
'media_sources' => [{"bitrate"=>12345, "label"=>"12 kbps", "src"=>"whatever man", "url"=>"whatever man"}],
"embedded_iframe_url"=>"http://test.host/media_objects_iframe/#{deleted_media_id}"
})
'media_sources' => [
{
'bitrate' => 12_345,
'label' => '12 kbps',
'src' => 'whatever man',
'url' => 'whatever man'
}
],
'embedded_iframe_url' => "http://test.host/media_objects_iframe/#{deleted_media_id}"
}
)
end
end
describe "GET 'index'" do
before do
# We don't actually want to ping kaltura during these tests
allow(MediaObject).to receive(:media_id_exists?).and_return(true)
allow_any_instance_of(MediaObject).to receive(:media_sources).and_return([{:url => "whatever man", :bitrate => 12345}])
allow_any_instance_of(MediaObject).to receive(:media_sources).and_return(
[{ url: 'whatever man', bitrate: 12_345 }]
)
end
it "should retrieve all MediaObjects user in the user's context" do
user_factory
user_session(@user)
mo1 = MediaObject.create!(:user_id => @user, :context => @user, :media_id => "test", :media_type => "video")
mo2 = MediaObject.create!(:user_id => @user, :context => @user, :media_id => "test2", :media_type => "audio", :title => "The Title")
mo3 = MediaObject.create!(:user_id => @user, :context => @user, :media_id => "test3", :user_entered_title => "User Title")
mo1 =
MediaObject.create!(user_id: @user, context: @user, media_id: 'test', media_type: 'video')
mo2 =
MediaObject.create!(
user_id: @user, context: @user, media_id: 'test2', media_type: 'audio', title: 'The Title'
)
mo3 =
MediaObject.create!(
user_id: @user, context: @user, media_id: 'test3', user_entered_title: 'User Title'
)
get 'index'
expect(json_parse(response.body)).to match_array([
{
"can_add_captions"=>true,
"created_at"=>mo2.created_at.as_json,
"media_id"=>"test2",
"media_sources"=>
[{"bitrate"=>12345,
"label"=>"12 kbps",
"src"=>"whatever man",
"url"=>"whatever man"}],
"media_tracks"=>[],
"title"=>"The Title",
"media_type"=>"audio",
"embedded_iframe_url"=>"http://test.host/media_objects_iframe/test2"
},
{
"can_add_captions"=>true,
"created_at"=>mo3.created_at.as_json,
"media_id"=>"test3",
"media_sources"=>
[{"bitrate"=>12345,
"label"=>"12 kbps",
"src"=>"whatever man",
"url"=>"whatever man"}],
"media_tracks"=>[],
"title"=>"User Title",
"media_type"=>nil,
"embedded_iframe_url"=>"http://test.host/media_objects_iframe/test3"
},
{
"can_add_captions"=>true,
"created_at"=>mo1.created_at.as_json,
"media_id"=>"test",
"media_sources"=>
[{"bitrate"=>12345,
"label"=>"12 kbps",
"src"=>"whatever man",
"url"=>"whatever man"}],
"media_tracks"=>[],
"title"=>"Untitled",
"media_type"=>"video",
"embedded_iframe_url"=>"http://test.host/media_objects_iframe/test"
}
])
expect(json_parse(response.body)).to match_array(
[
{
'can_add_captions' => true,
'created_at' => mo2.created_at.as_json,
'media_id' => 'test2',
'media_sources' => [
{
'bitrate' => 12_345,
'label' => '12 kbps',
'src' => 'whatever man',
'url' => 'whatever man'
}
],
'media_tracks' => [],
'title' => 'The Title',
'media_type' => 'audio',
'embedded_iframe_url' => 'http://test.host/media_objects_iframe/test2'
},
{
'can_add_captions' => true,
'created_at' => mo3.created_at.as_json,
'media_id' => 'test3',
'media_sources' => [
{
'bitrate' => 12_345,
'label' => '12 kbps',
'src' => 'whatever man',
'url' => 'whatever man'
}
],
'media_tracks' => [],
'title' => 'User Title',
'media_type' => nil,
'embedded_iframe_url' => 'http://test.host/media_objects_iframe/test3'
},
{
'can_add_captions' => true,
'created_at' => mo1.created_at.as_json,
'media_id' => 'test',
'media_sources' => [
{
'bitrate' => 12_345,
'label' => '12 kbps',
'src' => 'whatever man',
'url' => 'whatever man'
}
],
'media_tracks' => [],
'title' => 'Untitled',
'media_type' => 'video',
'embedded_iframe_url' => 'http://test.host/media_objects_iframe/test'
}
]
)
end
it "will not retrive items you did not create" do
it 'will not retrive items you did not create' do
user1 = user_factory
user2 = user_factory
user_session(user1)
MediaObject.create!(:user_id => user2, :context => user2, :media_id => "test")
MediaObject.create!(:user_id => user2, :context => user2, :media_id => "test2")
MediaObject.create!(:user_id => user2, :context => user2, :media_id => "test3")
MediaObject.create!(user_id: user2, context: user2, media_id: 'test')
MediaObject.create!(user_id: user2, context: user2, media_id: 'test2')
MediaObject.create!(user_id: user2, context: user2, media_id: 'test3')
get 'index'
expect(json_parse(response.body)).to eq([])
end
it "will exclude media_sources if asked to" do
it 'will exclude media_sources if asked to' do
user_factory
user_session(@user)
mo = MediaObject.create!(:user_id => @user, :context => @user, :media_id => "test", :media_type => "video")
mo =
MediaObject.create!(user_id: @user, context: @user, media_id: 'test', media_type: 'video')
get 'index', params: {:exclude => ["sources"]}
expect(json_parse(response.body)).to eq([
{
"can_add_captions"=>true,
"created_at"=>mo.created_at.as_json,
"media_id"=>"test",
"media_tracks"=>[],
"title"=>"Untitled",
"media_type"=>"video",
"embedded_iframe_url"=>"http://test.host/media_objects_iframe/test"
}
])
get 'index', params: { exclude: %w[sources] }
expect(json_parse(response.body)).to eq(
[
{
'can_add_captions' => true,
'created_at' => mo.created_at.as_json,
'media_id' => 'test',
'media_tracks' => [],
'title' => 'Untitled',
'media_type' => 'video',
'embedded_iframe_url' => 'http://test.host/media_objects_iframe/test'
}
]
)
end
it "will exclude media_tracks if asked to" do
it 'will exclude media_tracks if asked to' do
user_factory
user_session(@user)
mo = MediaObject.create!(:user_id => @user, :context => @user, :media_id => "test", :media_type => "video")
mo =
MediaObject.create!(user_id: @user, context: @user, media_id: 'test', media_type: 'video')
get 'index', params: {:exclude => ["tracks"]}
expect(json_parse(response.body)).to eq([
{
"can_add_captions"=>true,
"created_at"=>mo.created_at.as_json,
"media_id"=>"test",
"media_sources"=>
[{"bitrate"=>12345,
"label"=>"12 kbps",
"src"=>"whatever man",
"url"=>"whatever man"}],
"title"=>"Untitled",
"media_type"=>"video",
"embedded_iframe_url"=>"http://test.host/media_objects_iframe/test"
}
])
get 'index', params: { exclude: %w[tracks] }
expect(json_parse(response.body)).to eq(
[
{
'can_add_captions' => true,
'created_at' => mo.created_at.as_json,
'media_id' => 'test',
'media_sources' => [
{
'bitrate' => 12_345,
'label' => '12 kbps',
'src' => 'whatever man',
'url' => 'whatever man'
}
],
'title' => 'Untitled',
'media_type' => 'video',
'embedded_iframe_url' => 'http://test.host/media_objects_iframe/test'
}
]
)
end
it "will return media objects that do not belong to the user if course_id is supplied" do
it 'will return media objects that do not belong to the user if course_id is supplied' do
course_factory
teacher1 = teacher_in_course(:course => @course).user
teacher2 = teacher_in_course(:course => @course).user
teacher1 = teacher_in_course(course: @course).user
teacher2 = teacher_in_course(course: @course).user
user_session(teacher1)
# a media object associated with a canvas attachment
mo1 = MediaObject.create!(:user_id => teacher2, :context => @course, :media_id => "test")
@course.attachments.create!(:media_entry_id => "test", :uploaded_data => stub_png_data)
mo1 = MediaObject.create!(user_id: teacher2, context: @course, media_id: 'test')
@course.attachments.create!(media_entry_id: 'test', uploaded_data: stub_png_data)
# and a media object that's not
mo2 = MediaObject.create!(:user_id => teacher2, :context => @course, :media_id => "another_test")
mo2 = MediaObject.create!(user_id: teacher2, context: @course, media_id: 'another_test')
get 'index', params: {:course_id => @course.id, :exclude => ["sources", "tracks"]}
get 'index', params: { course_id: @course.id, exclude: %w[sources tracks] }
expect(json_parse(response.body)).to match_array([
{
"can_add_captions"=>true,
"created_at"=>mo1.created_at.as_json,
"media_id"=>"test",
"title"=>"Untitled",
"media_type"=>nil,
"embedded_iframe_url"=>"http://test.host/media_objects_iframe/test"
},
{
"can_add_captions"=>true,
"created_at"=>mo2.created_at.as_json,
"media_id"=>"another_test",
"title"=>"Untitled",
"media_type"=>nil,
"embedded_iframe_url"=>"http://test.host/media_objects_iframe/another_test"
}
])
expect(json_parse(response.body)).to match_array(
[
{
'can_add_captions' => true,
'created_at' => mo1.created_at.as_json,
'media_id' => 'test',
'title' => 'Untitled',
'media_type' => nil,
'embedded_iframe_url' => 'http://test.host/media_objects_iframe/test'
},
{
'can_add_captions' => true,
'created_at' => mo2.created_at.as_json,
'media_id' => 'another_test',
'title' => 'Untitled',
'media_type' => nil,
'embedded_iframe_url' => 'http://test.host/media_objects_iframe/another_test'
}
]
)
end
it "will paginate user media" do
it 'will paginate user media' do
user_factory
user_session(@user)
mo1 = mo2 = mo3 = nil
Timecop.freeze(30.seconds.ago) do
mo1 = MediaObject.create!(:user_id => @user, :context => @user, :media_id => "test", :media_type => "video")
mo1 =
MediaObject.create!(user_id: @user, context: @user, media_id: 'test', media_type: 'video')
end
Timecop.freeze(20.seconds.ago) do
mo2 = MediaObject.create!(:user_id => @user, :context => @user, :media_id => "test2", :media_type => "audio", :title => "The Title")
mo2 =
MediaObject.create!(
user_id: @user,
context: @user,
media_id: 'test2',
media_type: 'audio',
title: 'The Title'
)
end
Timecop.freeze(10.seconds.ago) do
mo3 = MediaObject.create!(:user_id => @user, :context => @user, :media_id => "test3", :user_entered_title => "User Title")
mo3 =
MediaObject.create!(
user_id: @user, context: @user, media_id: 'test3', user_entered_title: 'User Title'
)
end
get 'index', params: {:per_page => 2, :order_by => 'created_at', :order_dir => 'desc'}
expect(json_parse(response.body)).to match_array([
{
"can_add_captions"=>true,
"created_at"=>mo3.created_at.as_json,
"media_id"=>"test3",
"media_sources"=>
[{"bitrate"=>12345,
"label"=>"12 kbps",
"src"=>"whatever man",
"url"=>"whatever man"}],
"media_tracks"=>[],
"title"=>"User Title",
"media_type"=>nil,
"embedded_iframe_url"=>"http://test.host/media_objects_iframe/test3"
},
{
"can_add_captions"=>true,
"created_at"=>mo2.created_at.as_json,
"media_id"=>"test2",
"media_sources"=>
[{"bitrate"=>12345,
"label"=>"12 kbps",
"src"=>"whatever man",
"url"=>"whatever man"}],
"media_tracks"=>[],
"title"=>"The Title",
"media_type"=>"audio",
"embedded_iframe_url"=>"http://test.host/media_objects_iframe/test2"
}
])
get 'index', params: { per_page: 2, order_by: 'created_at', order_dir: 'desc' }
expect(json_parse(response.body)).to match_array(
[
{
'can_add_captions' => true,
'created_at' => mo3.created_at.as_json,
'media_id' => 'test3',
'media_sources' => [
{
'bitrate' => 12_345,
'label' => '12 kbps',
'src' => 'whatever man',
'url' => 'whatever man'
}
],
'media_tracks' => [],
'title' => 'User Title',
'media_type' => nil,
'embedded_iframe_url' => 'http://test.host/media_objects_iframe/test3'
},
{
'can_add_captions' => true,
'created_at' => mo2.created_at.as_json,
'media_id' => 'test2',
'media_sources' => [
{
'bitrate' => 12_345,
'label' => '12 kbps',
'src' => 'whatever man',
'url' => 'whatever man'
}
],
'media_tracks' => [],
'title' => 'The Title',
'media_type' => 'audio',
'embedded_iframe_url' => 'http://test.host/media_objects_iframe/test2'
}
]
)
get 'index', params: {:per_page => 2, :order_by => 'created_at', :order_dir => 'desc', :page => 2}
expect(json_parse(response.body)).to match_array([
{
"can_add_captions"=>true,
"created_at"=>mo1.created_at.as_json,
"media_id"=>"test",
"media_sources"=>
[{"bitrate"=>12345,
"label"=>"12 kbps",
"src"=>"whatever man",
"url"=>"whatever man"}],
"media_tracks"=>[],
"title"=>"Untitled",
"media_type"=>"video",
"embedded_iframe_url"=>"http://test.host/media_objects_iframe/test"
}
])
get 'index', params: { per_page: 2, order_by: 'created_at', order_dir: 'desc', page: 2 }
expect(json_parse(response.body)).to match_array(
[
{
'can_add_captions' => true,
'created_at' => mo1.created_at.as_json,
'media_id' => 'test',
'media_sources' => [
{
'bitrate' => 12_345,
'label' => '12 kbps',
'src' => 'whatever man',
'url' => 'whatever man'
}
],
'media_tracks' => [],
'title' => 'Untitled',
'media_type' => 'video',
'embedded_iframe_url' => 'http://test.host/media_objects_iframe/test'
}
]
)
end
it "will limit return to course media" do
it 'will limit return to course media' do
course_with_teacher_logged_in
mo1 = MediaObject.create!(:user_id => @user, :context => @course, :media_id => "in_course_with_att")
@course.attachments.create!(:media_entry_id => "in_course_with_att", :uploaded_data => stub_png_data)
mo1 = MediaObject.create!(user_id: @user, context: @course, media_id: 'in_course_with_att')
@course.attachments.create!(
media_entry_id: 'in_course_with_att', uploaded_data: stub_png_data
)
# That media objects associated with a deleted attachment are still returned
# is an artifact of changes made a long time ago so that Attachments from
# course copy share the media object.
# is an artifact of changes made a long time ago so that Attachments from
# course copy share the media object.
# see commit d27cf9f7d037571b2ee88c61be2ca72f19777b60
mo2 = MediaObject.create!(:user_id => @user, :context => @course, :media_id => "in_course_with_deleted_att")
deleted_att = @course.attachments.create!(:media_entry_id => "in_course_with_deleted_att", :uploaded_data => stub_png_data)
mo2 =
MediaObject.create!(
user_id: @user, context: @course, media_id: 'in_course_with_deleted_att'
)
deleted_att =
@course.attachments.create!(
media_entry_id: 'in_course_with_deleted_att', uploaded_data: stub_png_data
)
mo2.attachment_id = deleted_att.id # this normally happens via a delayed_job
mo2.save!
deleted_att.destroy!
MediaObject.create!(:user_id => @user, :context => @user, :media_id => "not_in_course")
MediaObject.create!(user_id: @user, context: @user, media_id: 'not_in_course')
get 'index', params: {:course_id => @course.id, :exclude => ["sources", "tracks"]}
get 'index', params: { course_id: @course.id, exclude: %w[sources tracks] }
expect(json_parse(response.body)).to match_array([
{
"media_id"=>"in_course_with_att",
"media_type"=>nil,
"created_at"=>mo1.created_at.as_json,
"title"=>"Untitled",
"can_add_captions"=>true,
"embedded_iframe_url"=>"http://test.host/media_objects_iframe/in_course_with_att"
},
{
"media_id"=>"in_course_with_deleted_att",
"media_type"=>nil,
"created_at"=>mo2.created_at.as_json,
"title"=>"Untitled",
"can_add_captions"=>true,
"embedded_iframe_url"=>"http://test.host/media_objects_iframe/in_course_with_deleted_att"
}
])
expect(json_parse(response.body)).to match_array(
[
{
'media_id' => 'in_course_with_att',
'media_type' => nil,
'created_at' => mo1.created_at.as_json,
'title' => 'Untitled',
'can_add_captions' => true,
'embedded_iframe_url' => 'http://test.host/media_objects_iframe/in_course_with_att'
},
{
'media_id' => 'in_course_with_deleted_att',
'media_type' => nil,
'created_at' => mo2.created_at.as_json,
'title' => 'Untitled',
'can_add_captions' => true,
'embedded_iframe_url' =>
'http://test.host/media_objects_iframe/in_course_with_deleted_att'
}
]
)
end
it "will paginate course media" do
it 'will paginate course media' do
course_with_teacher_logged_in
mo1 = mo2 = mo3 = nil
Timecop.freeze(30.seconds.ago) do
mo1 = MediaObject.create!(:user_id => @user, :context => @course, :media_id => "test", :media_type => "video")
mo1 =
MediaObject.create!(
user_id: @user, context: @course, media_id: 'test', media_type: 'video'
)
end
Timecop.freeze(20.seconds.ago) do
mo2 = MediaObject.create!(:user_id => @user, :context => @course, :media_id => "test2", :media_type => "audio", :title => "The Title")
mo2 =
MediaObject.create!(
user_id: @user,
context: @course,
media_id: 'test2',
media_type: 'audio',
title: 'The Title'
)
end
Timecop.freeze(10.seconds.ago) do
mo3 = MediaObject.create!(:user_id => @user, :context => @course, :media_id => "test3", :user_entered_title => "User Title")
mo3 =
MediaObject.create!(
user_id: @user, context: @course, media_id: 'test3', user_entered_title: 'User Title'
)
end
get 'index', params: {:course_id => @course.id, :per_page => 2, :order_by => 'created_at', :order_dir => 'desc'}
expect(json_parse(response.body)).to match_array([
{
"can_add_captions"=>true,
"created_at"=>mo3.created_at.as_json,
"media_id"=>"test3",
"media_sources"=>
[{"bitrate"=>12345,
"label"=>"12 kbps",
"src"=>"whatever man",
"url"=>"whatever man"}],
"media_tracks"=>[],
"title"=>"User Title",
"media_type"=>nil,
"embedded_iframe_url"=>"http://test.host/media_objects_iframe/test3"
},
{
"can_add_captions"=>true,
"created_at"=>mo2.created_at.as_json,
"media_id"=>"test2",
"media_sources"=>
[{"bitrate"=>12345,
"label"=>"12 kbps",
"src"=>"whatever man",
"url"=>"whatever man"}],
"media_tracks"=>[],
"title"=>"The Title",
"media_type"=>"audio",
"embedded_iframe_url"=>"http://test.host/media_objects_iframe/test2"
}
])
get 'index',
params: { course_id: @course.id, per_page: 2, order_by: 'created_at', order_dir: 'desc' }
expect(json_parse(response.body)).to match_array(
[
{
'can_add_captions' => true,
'created_at' => mo3.created_at.as_json,
'media_id' => 'test3',
'media_sources' => [
{
'bitrate' => 12_345,
'label' => '12 kbps',
'src' => 'whatever man',
'url' => 'whatever man'
}
],
'media_tracks' => [],
'title' => 'User Title',
'media_type' => nil,
'embedded_iframe_url' => 'http://test.host/media_objects_iframe/test3'
},
{
'can_add_captions' => true,
'created_at' => mo2.created_at.as_json,
'media_id' => 'test2',
'media_sources' => [
{
'bitrate' => 12_345,
'label' => '12 kbps',
'src' => 'whatever man',
'url' => 'whatever man'
}
],
'media_tracks' => [],
'title' => 'The Title',
'media_type' => 'audio',
'embedded_iframe_url' => 'http://test.host/media_objects_iframe/test2'
}
]
)
get 'index', params: {:course_id => @course.id, :per_page => 2, :order_by => 'created_at', :order_dir => 'desc', :page => 2}
expect(json_parse(response.body)).to match_array([
{
"can_add_captions"=>true,
"created_at"=>mo1.created_at.as_json,
"media_id"=>"test",
"media_sources"=>
[{"bitrate"=>12345,
"label"=>"12 kbps",
"src"=>"whatever man",
"url"=>"whatever man"}],
"media_tracks"=>[],
"title"=>"Untitled",
"media_type"=>"video",
"embedded_iframe_url"=>"http://test.host/media_objects_iframe/test"
}
])
get 'index',
params: {
course_id: @course.id, per_page: 2, order_by: 'created_at', order_dir: 'desc', page: 2
}
expect(json_parse(response.body)).to match_array(
[
{
'can_add_captions' => true,
'created_at' => mo1.created_at.as_json,
'media_id' => 'test',
'media_sources' => [
{
'bitrate' => 12_345,
'label' => '12 kbps',
'src' => 'whatever man',
'url' => 'whatever man'
}
],
'media_tracks' => [],
'title' => 'Untitled',
'media_type' => 'video',
'embedded_iframe_url' => 'http://test.host/media_objects_iframe/test'
}
]
)
end
it "will return a 404 if the given course_id doesn't exist" do
course_with_teacher_logged_in
mo1 = MediaObject.create!(:user_id => @user, :context => @course, :media_id => "in_course")
MediaObject.create!(:user_id => @user, :media_id => "not_in_course")
mo1 = MediaObject.create!(user_id: @user, context: @course, media_id: 'in_course')
MediaObject.create!(user_id: @user, media_id: 'not_in_course')
get 'index', params: {:course_id => 171717, :exclude => ["sources", "tracks"]}
get 'index', params: { course_id: 171_717, exclude: %w[sources tracks] }
expect(response.status.to_s).to eq("404")
expect(response.status.to_s).to eq('404')
end
it "will return user's media if context_type isn't 'course'" do
course_with_teacher_logged_in
mo1 = MediaObject.create!(:user_id => @user, :context => @course, :media_id => "in_course", :user_entered_title => "AAA")
mo2 = MediaObject.create!(:user_id => @user, :context => @user, :media_id => "not_in_course", :user_entered_title => "BBB")
mo1 =
MediaObject.create!(
user_id: @user, context: @course, media_id: 'in_course', user_entered_title: 'AAA'
)
mo2 =
MediaObject.create!(
user_id: @user, context: @user, media_id: 'not_in_course', user_entered_title: 'BBB'
)
get 'index', params: {:exclude => ["sources", "tracks"]}
get 'index', params: { exclude: %w[sources tracks] }
expect(json_parse(response.body)).to eq([
{
"can_add_captions"=>true,
"created_at"=>mo2.created_at.as_json,
"media_id"=>"not_in_course",
"title"=>"BBB",
"media_type"=>nil,
"embedded_iframe_url"=>"http://test.host/media_objects_iframe/not_in_course"
}
])
expect(json_parse(response.body)).to eq(
[
{
'can_add_captions' => true,
'created_at' => mo2.created_at.as_json,
'media_id' => 'not_in_course',
'title' => 'BBB',
'media_type' => nil,
'embedded_iframe_url' => 'http://test.host/media_objects_iframe/not_in_course'
}
]
)
end
it "will limit return to group media" do
it 'will limit return to group media' do
course_with_teacher_logged_in(active_all: true)
gcat = @course.group_categories.create!(:name => "My Group Category")
@group = Group.create!(:name => "some group", :group_category => gcat, :context => @course)
mo1 = MediaObject.create!(:user_id => @user, :context => @group, :media_id => "in_group")
gcat = @course.group_categories.create!(name: 'My Group Category')
@group = Group.create!(name: 'some group', group_category: gcat, context: @course)
mo1 = MediaObject.create!(user_id: @user, context: @group, media_id: 'in_group')
MediaObject.create!(:user_id => @user, :context => @course, :media_id => "in_course_with_att")
@course.attachments.create!(:media_entry_id => "in_course_with_att", :uploaded_data => stub_png_data)
MediaObject.create!(user_id: @user, context: @course, media_id: 'in_course_with_att')
@course.attachments.create!(
media_entry_id: 'in_course_with_att', uploaded_data: stub_png_data
)
MediaObject.create!(:user_id => @user, :context => @user, :media_id => "not_in_course")
MediaObject.create!(user_id: @user, context: @user, media_id: 'not_in_course')
get 'index', params: {:group_id => @group.id, :exclude => ["sources", "tracks"]}
get 'index', params: { group_id: @group.id, exclude: %w[sources tracks] }
expect(json_parse(response.body)).to match_array([
{
"media_id"=>"in_group",
"media_type"=>nil,
"created_at"=>mo1.created_at.as_json,
"title"=>"Untitled",
"can_add_captions"=>true,
"embedded_iframe_url"=>"http://test.host/media_objects_iframe/in_group"
}
])
expect(json_parse(response.body)).to match_array(
[
{
'media_id' => 'in_group',
'media_type' => nil,
'created_at' => mo1.created_at.as_json,
'title' => 'Untitled',
'can_add_captions' => true,
'embedded_iframe_url' => 'http://test.host/media_objects_iframe/in_group'
}
]
)
end
it "will sort by title" do
it 'will sort by title' do
course_with_teacher_logged_in
MediaObject.create!(:user_id => @user, :context => @user, :media_id => "test", :title => "ZZZ")
MediaObject.create!(:user_id => @user, :context => @user, :media_id => "test2", :title => "YYY")
MediaObject.create!(:user_id => @user, :context => @user, :media_id => "test3", :title => "XXX")
MediaObject.create!(user_id: @user, context: @user, media_id: 'test', title: 'ZZZ')
MediaObject.create!(user_id: @user, context: @user, media_id: 'test2', title: 'YYY')
MediaObject.create!(user_id: @user, context: @user, media_id: 'test3', title: 'XXX')
get 'index', params: {
:exclude => ["sources", "tracks"],
:sort => "title",
:order => "asc"
}
get 'index', params: { exclude: %w[sources tracks], sort: 'title', order: 'asc' }
result = json_parse(response.body)
expect(result[0]["title"]).to eq("XXX")
expect(result[1]["title"]).to eq("YYY")
expect(result[2]["title"]).to eq("ZZZ")
expect(result[0]['title']).to eq('XXX')
expect(result[1]['title']).to eq('YYY')
expect(result[2]['title']).to eq('ZZZ')
end
it "will sort by title or user_entered_title" do
it 'will sort by created_at' do
course_with_teacher_logged_in
MediaObject.create!(:user_id => @user, :context => @user, :media_id => "test", :title => "AAA", :user_entered_title => "ZZZ")
MediaObject.create!(:user_id => @user, :context => @user, :media_id => "test2", :title => "YYY", :user_entered_title => nil)
MediaObject.create!(:user_id => @user, :context => @user, :media_id => "test3", :title => "CCC", :user_entered_title => "XXX")
Timecop.freeze(2.seconds.ago) do
MediaObject.create!(user_id: @user, context: @user, media_id: 'test', title: 'AAA')
end
Timecop.freeze(1.seconds.ago) do
MediaObject.create!(user_id: @user, context: @user, media_id: 'test2', title: 'BBB')
end
MediaObject.create!(user_id: @user, context: @user, media_id: 'test3', title: 'CCC')
get 'index', params: {
:exclude => ["sources", "tracks"],
:sort => "title",
:order => "asc"
}
get 'index', params: { exclude: %w[sources tracks], sort: 'created_at', order: 'desc' }
result = json_parse(response.body)
expect(result[0]["title"]).to eq("XXX")
expect(result[1]["title"]).to eq("YYY")
expect(result[2]["title"]).to eq("ZZZ")
expect(result[0]['title']).to eq('CCC')
expect(result[1]['title']).to eq('BBB')
expect(result[2]['title']).to eq('AAA')
end
it "will sort by created_at" do
it 'will search by title' do
course_with_teacher_logged_in
Timecop.freeze(2.seconds.ago) { MediaObject.create!(:user_id => @user, :context => @user, :media_id => "test", :title => "AAA") }
Timecop.freeze(1.seconds.ago) { MediaObject.create!(:user_id => @user, :context => @user, :media_id => "test2", :title => "BBB") }
MediaObject.create!(:user_id => @user, :context => @user, :media_id => "test3", :title => "CCC")
MediaObject.create!(user_id: @user, context: @user, media_id: 'test', title: 'ZZZZ')
MediaObject.create!(user_id: @user, context: @user, media_id: 'test2', title: 'YYYY')
MediaObject.create!(user_id: @user, context: @user, media_id: 'test3', title: 'XXXX')
get 'index', params: {
:exclude => ["sources", "tracks"],
:sort => "created_at",
:order => "desc"
}
get 'index',
params: { exclude: %w[sources tracks], sort: 'title', order: 'asc', search_term: 'YYY' }
result = json_parse(response.body)
expect(result[0]["title"]).to eq("CCC")
expect(result[1]["title"]).to eq("BBB")
expect(result[2]["title"]).to eq("AAA")
expect(result[0]['title']).to eq('YYYY')
end
it 'will sort by title or user_entered_title' do
course_with_teacher_logged_in
MediaObject.create!(
user_id: @user, context: @user, media_id: 'test', title: 'AAA', user_entered_title: 'ZZZ'
)
MediaObject.create!(
user_id: @user, context: @user, media_id: 'test2', title: 'YYY', user_entered_title: nil
)
MediaObject.create!(
user_id: @user, context: @user, media_id: 'test3', title: 'CCC', user_entered_title: 'XXX'
)
get 'index', params: { exclude: %w[sources tracks], sort: 'title', order: 'asc' }
result = json_parse(response.body)
expect(result[0]['title']).to eq('XXX')
expect(result[1]['title']).to eq('YYY')
expect(result[2]['title']).to eq('ZZZ')
end
end
describe "PUT update_media_object" do
describe 'PUT update_media_object' do
it "returns a 401 if the MediaObject doesn't exist" do
course_with_teacher_logged_in
put 'update_media_object', params: {:media_object_id => "anything", :user_entered_title => "new title"}
put 'update_media_object',
params: { media_object_id: 'anything', user_entered_title: 'new title' }
assert_status(401)
end
it "returns a 401 if the MediaObject doesn't belong to the current user" do
course_with_teacher_logged_in
another_user = user_factory
MediaObject.create!(:user_id => another_user, :media_id => "another-video")
put 'update_media_object', params: {:media_object_id => "another-video", :user_entered_title => "new title"}
MediaObject.create!(user_id: another_user, media_id: 'another-video')
put 'update_media_object',
params: { media_object_id: 'another-video', user_entered_title: 'new title' }
assert_status(401)
end
it "requires a logged in user" do
it 'requires a logged in user' do
another_user = user_factory
MediaObject.create!(:user_id => another_user, :media_id => "another-video")
put 'update_media_object', params: {:media_object_id => "another-video", :user_entered_title => "new title"}
MediaObject.create!(user_id: another_user, media_id: 'another-video')
put 'update_media_object',
params: { media_object_id: 'another-video', user_entered_title: 'new title' }
assert_status(302) # redirect to login
end
it "returns the updated MediaObject" do
it 'returns the updated MediaObject' do
course_with_teacher_logged_in
MediaObject.create!(:user_id => @user, :media_id => "the-video", :title => "filename.mov")
put 'update_media_object', params: {:media_object_id => "the-video", :user_entered_title => "new title"}
MediaObject.create!(user_id: @user, media_id: 'the-video', title: 'filename.mov')
put 'update_media_object',
params: { media_object_id: 'the-video', user_entered_title: 'new title' }
assert_status(200)
json = JSON.parse(response.body)
expect(json["title"]).to eq("new title")
expect(json['title']).to eq('new title')
end
end
end