diff --git a/Changelog.md b/Changelog.md index a2041dddd3..d48cae06f5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -39,6 +39,7 @@ - Updated images: moved externally-hosted images into repository, ensured images are vertically centred and displayed as new paragraphs, and cropped images to remove blank space (#8053) ### 🔧 Internal changes +- Refactored annotation routes to respond with JSON rather than Javascript (#8127) - Removed `:blob` CSP exception for `img_src` for `groups_controller.rb` and `exam_templates_controller.rb` - Replaced `jcrop` with `cropperjs` for the exam template cover page crop selector (#8114) - Migrated `image_viewer.jsx` file to use `heic-convert` instead of `heic2any`; Updated CSP exceptions for controllers depending on `image_viewer.jsx` (#8100) diff --git a/app/controllers/annotations_controller.rb b/app/controllers/annotations_controller.rb index 700843391b..8e33cf6daa 100644 --- a/app/controllers/annotations_controller.rb +++ b/app/controllers/annotations_controller.rb @@ -21,7 +21,7 @@ def add_existing_annotation **params.to_unsafe_h.slice(*annotation_class.required_fields).symbolize_keys, **base_attributes ) - render :create + render json: @annotation.to_json(current_role: current_role) end def create @@ -62,6 +62,7 @@ def create **params.to_unsafe_h.slice(*annotation_class.required_fields).symbolize_keys, **base_attributes ) + render json: @annotation.to_json(current_role: current_role) end def destroy @@ -79,6 +80,7 @@ def destroy end end text = @annotation.annotation_text + annotation_text_id = @annotation.annotation_text_id text.destroy if text.annotation_category_id.nil? @annotation.destroy result.annotations.reload.each do |annot| @@ -86,6 +88,7 @@ def destroy annot.update(annotation_number: annot.annotation_number - 1) end end + render json: { id: @annotation.id, annotation_text_id: annotation_text_id } end def update @@ -99,7 +102,7 @@ def update end change_all = !params[:annotation_text] || !params[:annotation_text][:change_all] || - params[:annotation_text][:change_all] == '1' + params[:annotation_text][:change_all].to_s == '1' if change_all @annotation_text.update(content: params[:content]) else @@ -114,6 +117,7 @@ def update @annotation.update(annotation_text: new_text) end end + render json: { annotation: @annotation.reload.get_data(include_creator: true) } end protected diff --git a/app/javascript/Components/Result/result.jsx b/app/javascript/Components/Result/result.jsx index 26e1c2b52e..09479cc0cb 100644 --- a/app/javascript/Components/Result/result.jsx +++ b/app/javascript/Components/Result/result.jsx @@ -296,6 +296,14 @@ class Result extends React.Component { }; /* Callbacks for annotations */ + // Headers for annotation routes, which are submitted as JSON via fetch rather than + // through jQuery, so the CSRF token needs to be attached by hand. + jsonHeaders = () => ({ + "Content-Type": "application/json", + Accept: "application/json", + "X-CSRF-Token": document.querySelector('[name="csrf-token"]').content, + }); + newAnnotation = () => { const submission_file_id = this.leftPane.current.submissionFilePanel.current.state.selectedFile[1]; @@ -315,14 +323,18 @@ class Result extends React.Component { let onSubmit = formData => { let data = {...formData, ...metadata}; - return $.post({ - url: Routes.course_annotations_path(this.props.course_id), - data, - }).then(() => { - this.setState({ - annotationModal: INITIAL_ANNOTATION_MODAL_STATE, - }); - }); // Resetting back to original + return fetch(Routes.course_annotations_path(this.props.course_id), { + method: "POST", + headers: this.jsonHeaders(), + body: JSON.stringify(data), + }) + .then(response => response.json()) + .then(json => { + this.addAnnotation(json.annotation, json.mark_update); + this.setState({ + annotationModal: INITIAL_ANNOTATION_MODAL_STATE, + }); + }); // Resetting back to original }; this.setState({ @@ -355,27 +367,20 @@ class Result extends React.Component { } }; - addAnnotation = ( - annotation, - criterion_id = null, - mark_value = null, - new_subtotal = null, - new_total = null, - new_num_marked = null - ) => { + addAnnotation = (annotation, markUpdate = null) => { this.setState({annotations: this.state.annotations.concat([annotation])}); - if (!!criterion_id) { + if (markUpdate) { let newMarks = [...this.state.marks]; - let i = newMarks.findIndex(m => m.id === criterion_id); + let i = newMarks.findIndex(m => m.id === markUpdate.criterion_id); if (i >= 0) { newMarks[i] = {...newMarks[i]}; - newMarks[i].mark = mark_value; + newMarks[i].mark = markUpdate.mark; this.setState({ marks: newMarks, - subtotal: new_subtotal, - total: new_total, - num_marked: new_num_marked, + subtotal: markUpdate.subtotal, + total: markUpdate.total, + num_marked: markUpdate.num_marked, }); } } @@ -404,7 +409,15 @@ class Result extends React.Component { data = this.extend_with_selection_data(data); if (data) { - $.post(Routes.add_existing_annotation_course_annotations_path(this.props.course_id), data); + fetch(Routes.add_existing_annotation_course_annotations_path(this.props.course_id), { + method: "POST", + headers: this.jsonHeaders(), + body: JSON.stringify(data), + }) + .then(response => response.json()) + .then(json => { + this.addAnnotation(json.annotation, json.mark_update); + }); } }; @@ -424,7 +437,15 @@ class Result extends React.Component { data = this.extend_with_selection_data(data); if (data) { - $.post(Routes.course_annotations_path(this.props.course_id), data, undefined, "script"); + fetch(Routes.course_annotations_path(this.props.course_id), { + method: "POST", + headers: this.jsonHeaders(), + body: JSON.stringify(data), + }) + .then(response => response.json()) + .then(json => { + this.addAnnotation(json.annotation, json.mark_update); + }); } }; @@ -468,18 +489,24 @@ class Result extends React.Component { let onSubmit = formData => { let data = {...formData, ...metadata}; - $.ajax({ - url: Routes.course_annotation_path(this.props.course_id, annot_id), - data, + return fetch(Routes.course_annotation_path(this.props.course_id, annot_id), { method: "PUT", - dataType: "json", - }).always(() => { - this.setState({ - annotationModal: INITIAL_ANNOTATION_MODAL_STATE, + headers: this.jsonHeaders(), + body: JSON.stringify(data), + }) + .then(response => (response.ok ? response.json() : null)) + .then(json => { + if (json) { + this.updateAnnotation(json.annotation); + } + }) + .finally(() => { + this.setState({ + annotationModal: INITIAL_ANNOTATION_MODAL_STATE, + }); + this.refreshAnnotations(); + this.refreshAnnotationCategories(); }); - this.refreshAnnotations(); - this.refreshAnnotationCategories(); - }); }; let annotation = this.state.annotations.find( @@ -543,7 +570,7 @@ class Result extends React.Component { } } - destroyAnnotation(annotation_id, range, annotation_text_id) { + destroyAnnotation(annotation_id, annotation_text_id) { if ( !!window.annotation_manager && window.annotation_manager.annotation_text_manager.annotationTextExists(annotation_text_id) @@ -565,15 +592,21 @@ class Result extends React.Component { } removeAnnotation = annot_id => { - $.ajax({ - url: Routes.course_annotation_path(this.props.course_id, annot_id), + fetch(Routes.course_annotation_path(this.props.course_id, annot_id), { method: "DELETE", - data: { + headers: this.jsonHeaders(), + body: JSON.stringify({ result_id: this.state.result_id, assignment_id: this.state.assignment_id, - }, - dataType: "script", - }).then(this.fetchData); + }), + }) + .then(response => (response.ok ? response.json() : null)) + .then(json => { + if (json) { + this.destroyAnnotation(json.id, json.annotation_text_id); + } + }) + .then(this.fetchData); }; /* Callbacks for RightPane */ @@ -1026,12 +1059,9 @@ class Result extends React.Component { submission_files={this.state.submission_files} student_view={this.props.role === "Student"} newAnnotation={this.newAnnotation} - addAnnotation={this.addAnnotation} addExistingAnnotation={this.addExistingAnnotation} editAnnotation={this.editAnnotation} - updateAnnotation={this.updateAnnotation} removeAnnotation={this.removeAnnotation} - destroyAnnotation={this.destroyAnnotation} rmd_convert_enabled={this.props.rmd_convert_enabled} /> diff --git a/app/models/annotation.rb b/app/models/annotation.rb index 9d81997023..f0fce5a5f7 100644 --- a/app/models/annotation.rb +++ b/app/models/annotation.rb @@ -105,6 +105,28 @@ def get_data(include_creator: false) data end + # Serializes this annotation for the Result component. When the annotation carries a + # deduction that isn't overridden, also includes the updated mark/subtotal/total so + # the front end can reflect the deduction without a full refetch. + def to_json(current_role: nil) + data = { annotation: get_data(include_creator: true) } + + criterion = annotation_text.annotation_category&.flexible_criterion + mark = result.marks.find_by(criterion: criterion) + unless annotation_text.deduction.nil? || annotation_text.deduction == 0 || mark.override + grader_id = current_role&.instructor? ? nil : current_role&.id + data[:mark_update] = { + criterion_id: criterion.id, + mark: mark.mark, + subtotal: result.get_subtotal, + total: result.get_total_mark, + num_marked: result.grouping.assignment.get_num_marked(grader_id) + } + end + + data.to_json + end + private # check if the submission file is associated with a remark result or a released result diff --git a/app/views/annotations/create.js.erb b/app/views/annotations/create.js.erb deleted file mode 100755 index 6d402a9027..0000000000 --- a/app/views/annotations/create.js.erb +++ /dev/null @@ -1,52 +0,0 @@ -(function () { - annotation_details = { - id: <%= @annotation.id %>, - creator: '<%= "#{@annotation.creator.display_name}" %>', - filename: '<%= @annotation.submission_file.filename %>', - path: '<%= @annotation.submission_file.path.split('/', 2)[1] %>', - submission_file_id: <%= @annotation.submission_file_id %>, - content: '<%= @annotation.annotation_text.escape_content %>', - annotation_category: '<%= @annotation.annotation_text.annotation_category&.annotation_category_name %>', - annotation_text_id: <%= @annotation.annotation_text_id %>, - criterion_id: <%= @annotation.annotation_text.annotation_category&.flexible_criterion_id || 'undefined' %>, - criterion_name: '<%= @annotation.annotation_text.annotation_category&.flexible_criterion&.name %>' || undefined, - deduction: <%= @annotation.annotation_text.deduction || 'undefined' %>, - type: '<%= @annotation.class.name %>', - number: <%= @annotation.annotation_number %>, - is_remark: <%= @annotation.is_remark %>, - line_start: <%= @annotation.line_start || 'undefined' %>, - line_end: <%= @annotation.line_end || 'undefined' %>, - column_start: <%= @annotation.column_start || 'undefined' %>, - column_end: <%= @annotation.column_end || 'undefined' %>, - page: <%= @annotation.page || 'undefined' %>, - x_range: { - start: <%= [@annotation.x1, @annotation.x2].min || 'undefined' %>, - end: <%= [@annotation.x1, @annotation.x2].max || 'undefined' %> - }, - y_range: { - start: <%= [@annotation.y1, @annotation.y2].min || 'undefined' %>, - end: <%= [@annotation.y1, @annotation.y2].max || 'undefined' %> - }, - start_node: <%= (@annotation.start_node || '').to_json.html_safe %>, - start_offset: <%= @annotation.start_offset || 'undefined' %>, - end_node: <%= (@annotation.end_node || '').to_json.html_safe %>, - end_offset: <%= @annotation.end_offset || 'undefined' %> - }; - - <% deduction = @annotation.annotation_text.deduction %> - <% mark = @annotation.result - .marks - .find_by(criterion: @annotation.annotation_text&.annotation_category&.flexible_criterion) %> - <% if deduction.nil? || deduction == 0 || mark.override %> - resultComponent.current.addAnnotation(annotation_details); - <% else %> - resultComponent.current.addAnnotation( - annotation_details, - <%= @annotation.annotation_text.annotation_category.flexible_criterion_id %>, - <%= mark.mark %>, - <%= @annotation.result.get_subtotal %>, - <%= @annotation.result.get_total_mark %>, - <%= @annotation.result.grouping.assignment.get_num_marked(@current_role.instructor? ? nil : @current_role.id) %> - ); - <% end %> -})(); diff --git a/app/views/annotations/destroy.js.erb b/app/views/annotations/destroy.js.erb deleted file mode 100644 index 68c3018a62..0000000000 --- a/app/views/annotations/destroy.js.erb +++ /dev/null @@ -1,6 +0,0 @@ -resultComponent.current.destroyAnnotation( - <%= @annotation.id %>, - { start: '<%= @annotation.line_start %>', - end: '<%= @annotation.line_end %>' }, - <%= @annotation.annotation_text.id %> -); diff --git a/app/views/annotations/update.js.erb b/app/views/annotations/update.js.erb deleted file mode 100644 index 501f45c751..0000000000 --- a/app/views/annotations/update.js.erb +++ /dev/null @@ -1,6 +0,0 @@ -resultComponent.current.updateAnnotation({ - id: <%= @annotation.id %>, - annotation_text_id: <%= @annotation.annotation_text.id %>, - content: '<%= @annotation.annotation_text.escape_content %>', - annotation_category: '<%= @annotation.annotation_text.annotation_category&.annotation_category_name %>', -}); diff --git a/spec/controllers/annotations_controller_spec.rb b/spec/controllers/annotations_controller_spec.rb index 800c9088ab..59d4621fdd 100644 --- a/spec/controllers/annotations_controller_spec.rb +++ b/spec/controllers/annotations_controller_spec.rb @@ -44,7 +44,7 @@ :add_existing_annotation, params: { annotation_text_id: annotation_text.id, submission_file_id: submission_file.id, line_start: 1, line_end: 1, column_start: 1, column_end: 1, result_id: result.id, course_id: course.id }, - format: :js + format: :json expect(response).to have_http_status(:success) expect(result.annotations.reload.size).to eq 1 @@ -55,7 +55,7 @@ :add_existing_annotation, params: { annotation_text_id: annotation_text.id, submission_file_id: image_submission_file.id, x1: 0, x2: 1, y1: 0, y2: 1, result_id: result.id, course_id: course.id }, - format: :js + format: :json expect(response).to have_http_status(:success) expect(result.annotations.reload.size).to eq 1 @@ -67,7 +67,7 @@ params: { annotation_text_id: annotation_text.id, submission_file_id: notebook_submission_file.id, start_node: 'a', start_offset: 1, end_node: 'b', end_offset: 0, result_id: result.id, course_id: course.id }, - format: :js + format: :json expect(response).to have_http_status(:success) expect(result.annotations.reload.size).to eq 1 @@ -82,7 +82,7 @@ params: { annotation_text_id: annotation_text.id, submission_file_id: rmd_submission_file.id, start_node: 'a', start_offset: 1, end_node: 'b', end_offset: 0, result_id: result.id, course_id: course.id }, - format: :js + format: :json expect(response).to have_http_status(:success) expect(result.annotations.reload.size).to eq 1 @@ -98,7 +98,7 @@ params: { annotation_text_id: annotation_text.id, submission_file_id: rmd_submission_file.id, line_start: 1, line_end: 1, column_start: 1, column_end: 1, result_id: result.id, course_id: course.id }, - format: :js + format: :json expect(response).to have_http_status(:success) expect(result.annotations.reload.size).to eq 1 @@ -110,7 +110,7 @@ :add_existing_annotation, params: { annotation_text_id: annotation_text.id, submission_file_id: pdf_submission_file.id, x1: 0, x2: 1, y1: 0, y2: 1, page: 1, result_id: result.id, course_id: course.id }, - format: :js + format: :json expect(response).to have_http_status(:success) expect(result.annotations.reload.size).to eq 1 @@ -122,7 +122,7 @@ :add_existing_annotation, params: { annotation_text_id: annotation_text.id, submission_file_id: submission_file.id, result_id: result.id, course_id: course.id }, - format: :js + format: :json end.to raise_error(ActiveRecord::RecordInvalid) expect(result.annotations.reload).to be_empty end @@ -135,10 +135,16 @@ params: { content: annotation_text.content, category_id: annotation_category.id, submission_file_id: submission_file.id, line_start: 1, line_end: 1, column_start: 1, column_end: 1, result_id: result.id, assignment_id: assignment.id, course_id: course.id }, - format: :js + format: :json expect(response).to have_http_status(:success) expect(result.annotations.reload.size).to eq 1 + json = response.parsed_body + created_annotation = result.annotations.first + expect(json['annotation']['id']).to eq created_annotation.id + expect(json['annotation']['content']).to eq annotation_text.content + expect(json['annotation']['line_start']).to eq 1 + expect(json['annotation']['line_end']).to eq 1 end it 'successfully uses an existing one-time-only text annotation' do @@ -148,7 +154,7 @@ annotation_text_id: annotation_text_oto.id, category_id: nil, submission_file_id: submission_file.id, line_start: 1, line_end: 1, column_start: 1, column_end: 1, result_id: result.id, assignment_id: assignment.id, course_id: course.id }, - format: :js + format: :json expect(response).to have_http_status(:success) expect(result.annotations.reload.size).to eq 1 @@ -160,7 +166,7 @@ annotation_text_id: annotation_text_oto.id, category_id: nil, submission_file_id: submission_file.id, line_start: 2, line_end: 2, column_start: 2, column_end: 2, result_id: result.id, assignment_id: assignment.id, course_id: course.id }, - format: :js + format: :json expect(response).to have_http_status(:success) expect(result.annotations.reload.size).to eq 2 expect(AnnotationText.all.size).to eq 3 @@ -173,7 +179,7 @@ annotation_text_id: annotation_text.id, category_id: annotation_category.id, submission_file_id: submission_file.id, line_start: 1, line_end: 1, column_start: 1, column_end: 1, result_id: result.id, assignment_id: assignment.id, course_id: course.id }, - format: :js + format: :json expect(response).to have_http_status(:success) expect(result.annotations.reload.size).to eq 1 @@ -185,7 +191,7 @@ annotation_text_id: annotation_text.id, category_id: annotation_category.id, submission_file_id: submission_file.id, line_start: 2, line_end: 2, column_start: 2, column_end: 2, result_id: result.id, assignment_id: assignment.id, course_id: course.id }, - format: :js + format: :json expect(response).to have_http_status(:success) expect(result.annotations.reload.size).to eq 2 expect(AnnotationText.all.size).to eq 1 @@ -198,7 +204,7 @@ annotation_text_id: annotation_text_oto.id, category_id: annotation_category.id, submission_file_id: submission_file.id, line_start: 1, line_end: 1, column_start: 1, column_end: 1, result_id: result.id, assignment_id: assignment.id, course_id: course.id }, - format: :js + format: :json expect(response).to have_http_status(:success) expect(result.annotations.reload.size).to eq 1 @@ -213,7 +219,7 @@ annotation_text_id: annotation_text.id, category_id: new_category.id, submission_file_id: submission_file.id, line_start: 1, line_end: 1, column_start: 1, column_end: 1, result_id: result.id, assignment_id: assignment.id, course_id: course.id }, - format: :js + format: :json expect(response).to have_http_status(:success) expect(result.annotations.reload.size).to eq 1 @@ -227,7 +233,7 @@ annotation_text_id: annotation_text.id, category_id: nil, submission_file_id: submission_file.id, line_start: 1, line_end: 1, column_start: 1, column_end: 1, result_id: result.id, assignment_id: assignment.id, course_id: course.id }, - format: :js + format: :json expect(response).to have_http_status(:success) expect(result.annotations.reload.size).to eq 1 @@ -243,10 +249,13 @@ params: { content: annotation_text.content, category_id: annotation_category.id, submission_file_id: image_submission_file.id, x1: 0, x2: 1, y1: 0, y2: 1, result_id: result.id, assignment_id: assignment.id, course_id: course.id }, - format: :js + format: :json expect(response).to have_http_status(:ok) expect(result.annotations.reload.size).to eq 1 + json = response.parsed_body + expect(json['annotation']['x_range']).to eq({ 'start' => 0, 'end' => 1 }) + expect(json['annotation']['y_range']).to eq({ 'start' => 0, 'end' => 1 }) end it 'successfully creates a PDF annotation' do @@ -255,7 +264,7 @@ params: { content: annotation_text.content, category_id: annotation_category.id, submission_file_id: pdf_submission_file.id, x1: 0, x2: 1, y1: 0, y2: 1, page: 1, result_id: result.id, assignment_id: assignment.id, course_id: course.id }, - format: :js + format: :json expect(response).to have_http_status(:ok) expect(result.annotations.reload.size).to eq 1 @@ -268,7 +277,7 @@ submission_file_id: notebook_submission_file.id, start_node: 'a', start_offset: 1, end_node: 'b', end_offset: 0, result_id: result.id, assignment_id: assignment.id, course_id: course.id }, - format: :js + format: :json expect(response).to have_http_status(:success) expect(result.annotations.reload.size).to eq 1 @@ -283,7 +292,7 @@ params: { annotation_text_id: annotation_text.id, submission_file_id: rmd_submission_file.id, start_node: 'a', start_offset: 1, end_node: 'b', end_offset: 0, result_id: result.id, course_id: course.id }, - format: :js + format: :json expect(response).to have_http_status(:success) expect(result.annotations.reload.size).to eq 1 @@ -299,7 +308,7 @@ params: { annotation_text_id: annotation_text.id, submission_file_id: rmd_submission_file.id, line_start: 1, line_end: 1, column_start: 1, column_end: 1, result_id: result.id, course_id: course.id }, - format: :js + format: :json expect(response).to have_http_status(:success) expect(result.annotations.reload.size).to eq 1 @@ -316,12 +325,39 @@ params: { content: 'I like icecream!', category_id: category.id, submission_file_id: submission_file.id, line_start: 1, line_end: 1, column_start: 1, column_end: 1, result_id: result.id, assignment_id: assignment.id, course_id: course.id }, - format: :js + format: :json expect(response).to have_http_status(:success) expect(result.annotations.reload.size).to eq 2 expect(result.annotations.joins(:annotation_text).where('annotation_texts.deduction': 0).size).to eq 1 end + + it 'includes a mark_update when reusing a deductive annotation text that is not overridden' do + assignment = create(:assignment_with_deductive_annotations) + result = assignment.groupings.first.current_result + existing_annotation = result.annotations.first + deductive_text = existing_annotation.annotation_text + submission_file = create(:submission_file, submission: result.submission) + + post_as user, + :add_existing_annotation, + params: { annotation_text_id: deductive_text.id, submission_file_id: submission_file.id, + line_start: 2, line_end: 2, column_start: 1, column_end: 1, result_id: result.id, + course_id: course.id }, + format: :json + + expect(response).to have_http_status(:success) + json = response.parsed_body + criterion = deductive_text.annotation_category.flexible_criterion + mark = result.marks.find_by(criterion: criterion) + expect(json['mark_update']).to eq( + 'criterion_id' => criterion.id, + 'mark' => mark.mark, + 'subtotal' => result.get_subtotal, + 'total' => result.get_total_mark, + 'num_marked' => assignment.get_num_marked(user.instructor? ? nil : user.id) + ) + end end describe '#destroy' do @@ -337,10 +373,13 @@ :destroy, params: { id: anno.id, submission_file_id: submission_file.id, assignment_id: assignment.id, result_id: result.id, course_id: course.id }, - format: :js + format: :json expect(response).to have_http_status(:success) expect(result.annotations.reload.size).to eq 0 + json = response.parsed_body + expect(json['id']).to eq anno.id + expect(json['annotation_text_id']).to eq anno.annotation_text_id end it 'destroys an annotation when there are multiple annotations for the result' do @@ -359,7 +398,7 @@ :destroy, params: { id: annotations[1].id, submission_file_id: submission_file.id, assignment_id: assignment.id, result_id: result.id, course_id: course.id }, - format: :js + format: :json expect(response).to have_http_status(:success) @@ -382,7 +421,7 @@ :destroy, params: { id: anno.id, submission_file_id: submission_file.id, assignment_id: assignment.id, result_id: result.id, course_id: course.id }, - format: :js + format: :json expect(AnnotationText.exists?(annotation_text.id)).to be true end @@ -400,7 +439,7 @@ :destroy, params: { id: anno.id, submission_file_id: submission_file.id, assignment_id: assignment.id, result_id: result.id, course_id: course.id }, - format: :js + format: :json expect(AnnotationText.exists?(new_text.id)).to be false end @@ -418,9 +457,12 @@ :update, params: { id: anno.id, assignment_id: assignment.id, submission_file_id: submission_file.id, result_id: result.id, content: 'new content', course_id: course.id }, - format: :js + format: :json expect(response).to have_http_status(:success) expect(anno.annotation_text.reload.content).to eq 'new content' + json = response.parsed_body + expect(json['annotation']['id']).to eq anno.id + expect(json['annotation']['content']).to eq 'new content' end it 'successfully updates a singular annotation text' do @@ -444,7 +486,7 @@ params: { id: anno1.id, assignment_id: assignment.id, submission_file_id: submission_file.id, result_id: result.id, content: 'new content', annotation_text: { change_all: '0' }, course_id: course.id }, - format: :js + format: :json expect(response).to have_http_status(:success) expect(anno1.reload.annotation_text.reload.content).to eq 'new content' expect(anno2.reload.annotation_text.reload.content).not_to eq 'new content' @@ -470,7 +512,7 @@ result_id: result.id, course_id: course.id, assignment_id: assignment.id }, - format: :js + format: :json expect(annotation.reload.annotation_text.content).to eq 'New content!' end @@ -485,7 +527,7 @@ course_id: course.id, result_id: other_grouping.current_result.id, assignment_id: assignment.id }, - format: :js + format: :json expect(response).to have_http_status(:bad_request) expect(annotation.reload.annotation_text.content).not_to eq 'New content!' end @@ -497,7 +539,7 @@ result_id: result.id, course_id: course.id, assignment_id: assignment.id }, - format: :js + format: :json expect(result.reload.annotations.size).to eq 0 end @@ -509,7 +551,7 @@ result_id: result.id, course_id: course.id, assignment_id: assignment.id }, - format: :js + format: :json expect(result.reload.annotations.size).to eq 0 end end @@ -533,7 +575,7 @@ result_id: result.id, course_id: course.id, assignment_id: assignment.id }, - format: :js + format: :json expect(response).to have_http_status(:bad_request) expect(annotation.reload.annotation_text.content).not_to eq 'New content!' end @@ -550,7 +592,7 @@ result_id: result.id, course_id: course.id, assignment_id: assignment.id }, - format: :js + format: :json expect(response).to have_http_status(:bad_request) expect(annotation.reload.annotation_text.content).not_to eq 'New content!' end @@ -565,7 +607,7 @@ result_id: result.id, course_id: course.id, assignment_id: assignment.id }, - format: :js + format: :json expect(response).to have_http_status(:bad_request) expect(result.reload.annotations.size).to eq 1 end @@ -580,7 +622,7 @@ result_id: result.id, course_id: course.id, assignment_id: assignment.id }, - format: :js + format: :json expect(result.reload.annotations.size).to eq 0 end @@ -592,7 +634,7 @@ result_id: result.id, course_id: course.id, assignment_id: assignment.id }, - format: :js + format: :json expect(response).to have_http_status(:bad_request) expect(result.reload.annotations.size).to eq 1 end @@ -604,7 +646,7 @@ result_id: result.id, course_id: course.id, assignment_id: assignment.id }, - format: :js + format: :json expect(result.reload.annotations.size).to eq 0 end end @@ -619,7 +661,7 @@ :add_existing_annotation, params: { annotation_text_id: annotation_text.id, submission_file_id: submission_file.id, line_start: 1, line_end: 1, column_start: 1, column_end: 1, result_id: result.id, course_id: course.id }, - format: :js + format: :json expect(subject).to respond_with(:forbidden) expect(result.annotations.reload.size).to eq 0 @@ -633,7 +675,7 @@ params: { content: annotation_text.content, category_id: annotation_category.id, submission_file_id: submission_file.id, line_start: 1, line_end: 1, column_start: 1, column_end: 1, result_id: result.id, assignment_id: assignment.id, course_id: course.id }, - format: :js + format: :json expect(subject).to respond_with(:forbidden) expect(result.annotations.reload.size).to eq 0 @@ -652,7 +694,7 @@ :destroy, params: { id: anno.id, submission_file_id: submission_file.id, assignment_id: assignment.id, result_id: result.id, course_id: course.id }, - format: :js + format: :json expect(subject).to respond_with(:forbidden) expect(result.annotations.reload.size).to eq 1 @@ -671,7 +713,7 @@ :update, params: { id: anno.id, assignment_id: assignment.id, submission_file_id: submission_file.id, result_id: result.id, content: 'new content', course_id: course.id }, - format: :js + format: :json expect(subject).to respond_with(:forbidden) expect(anno.annotation_text.reload.content).not_to eq 'new content' end