From 533b02544f74729a439cb505659b0421ecdd15d7 Mon Sep 17 00:00:00 2001 From: Nick Houle Date: Tue, 5 Feb 2019 13:19:50 -0700 Subject: [PATCH] Update Progress Widget UI Fixes: GOOF-701 Note: - We are no longer using the `workflow_state` attribute form the Attachment model. We are using AttachmentUploadStatus that is joined to the Attachment model to retrieve the same results. We are now using Redis to track the state of the Attachment per Submission which will allow us to give a more accurate view of the Attachment state as it is being processed. Test plan: - Compile assets for canvas - Boot up the docker container - Navigate to a course with the google lti installed - As a student submit an assignment from google drive - On the assignment detials, submission details and grade summary pages (found on the invision links in the ticket) confirm you see the progress widgets and the files correct upload status - On the submission details page in the grade book confirm you can see the icons - In speedgrader confirm you can see the correct icons on the right hand sidebar AND confirm they change accrodignly as you change the submission selected from the dropdown if there are multiple submissions Change-Id: I6c1152cb7b450c3c2e3a2ca810233fc222c0967a Reviewed-on: https://gerrit.instructure.com/180605 Tested-by: Jenkins Reviewed-by: Joshua Orr QA-Review: Deepeeca Soundarrajan Product-Review: Jesse Poulos --- .../gradebook/SubmissionCell.coffee | 6 +- .../lti/submissions_api_controller.rb | 1 + app/jsx/bundles/progress_pill.js | 4 +- app/models/speed_grader/assignment.rb | 1 + .../grade_summary_assignment_presenter.rb | 8 +- .../assignments/_submission_sidebar.html.erb | 2 +- app/views/jst/_submission_detail.handlebars | 4 +- lib/api/v1/attachment.rb | 3 +- public/javascripts/speed_grader.js | 55 +------- spec/apis/file_uploads_spec_helper.rb | 2 + spec/apis/lti/submissions_api_spec.rb | 2 + spec/apis/v1/conversations_api_spec.rb | 2 + spec/apis/v1/discussion_topics_api_spec.rb | 3 + spec/apis/v1/files_controller_api_spec.rb | 3 + spec/apis/v1/submissions_api_spec.rb | 4 + .../gradebook/SubmissionCellSpec.js | 22 +++ spec/javascripts/jsx/speed_graderSpec.js | 129 ++++++++++++++++++ ...grade_summary_assignment_presenter_spec.rb | 19 +-- 18 files changed, 200 insertions(+), 70 deletions(-) diff --git a/app/coffeescripts/gradebook/SubmissionCell.coffee b/app/coffeescripts/gradebook/SubmissionCell.coffee index 8aa313322c7..885ddc8fb76 100644 --- a/app/coffeescripts/gradebook/SubmissionCell.coffee +++ b/app/coffeescripts/gradebook/SubmissionCell.coffee @@ -186,10 +186,10 @@ define [ @submissionIcon: (submission_type, attachments) -> klass = SubmissionCell.iconFromSubmissionType(submission_type) if attachments? - workflow_state = attachments[0].workflow_state - if workflow_state == "pending_upload" + upload_status = attachments[0].upload_status + if upload_status == "pending" klass = "upload" - else if workflow_state == "errored" + else if upload_status == "failed" klass = "warning" "" diff --git a/app/controllers/lti/submissions_api_controller.rb b/app/controllers/lti/submissions_api_controller.rb index 912c97263a1..8fbe55ba4ae 100644 --- a/app/controllers/lti/submissions_api_controller.rb +++ b/app/controllers/lti/submissions_api_controller.rb @@ -242,6 +242,7 @@ module Lti def attachment_json(attachment) attachment_attributes = %w(id display_name filename content-type size created_at updated_at) attach = filtered_json(model: attachment, whitelist: attachment_attributes) + attach[:upload_status] = AttachmentUploadStatus.upload_status(attachment) attach[:url] = attachment_url(attachment) attach end diff --git a/app/jsx/bundles/progress_pill.js b/app/jsx/bundles/progress_pill.js index cafbfc9226d..2776fce34b8 100644 --- a/app/jsx/bundles/progress_pill.js +++ b/app/jsx/bundles/progress_pill.js @@ -26,9 +26,9 @@ import IconWarning from '@instructure/ui-icons/lib/Line/IconWarning' const presenter = document.querySelectorAll(".assignment_presenter_for_submission") const progressIcon = (presenterObject) => { switch (presenterObject.innerText) { - case 'pending_upload': + case 'pending': return [, I18n.t("Uploading Submission")] - case 'errored': + case 'failed': return [, I18n.t("Submission Failed to Submit")] default: return null diff --git a/app/models/speed_grader/assignment.rb b/app/models/speed_grader/assignment.rb index 4aae881b6b2..83a50115cf0 100644 --- a/app/models/speed_grader/assignment.rb +++ b/app/models/speed_grader/assignment.rb @@ -241,6 +241,7 @@ module SpeedGrader json[:attachment][:crocodoc_url] = a.crocodoc_url(@current_user, url_opts) json[:attachment][:submitted_to_crocodoc] = a.crocodoc_document.present? json[:attachment][:hijack_crocodoc_session] = a.crocodoc_document.present? && @should_migrate_to_canvadocs + json[:attachment][:upload_status] = AttachmentUploadStatus.upload_status(a) end end end diff --git a/app/presenters/grade_summary_assignment_presenter.rb b/app/presenters/grade_summary_assignment_presenter.rb index 5f19b800138..89ce8020324 100644 --- a/app/presenters/grade_summary_assignment_presenter.rb +++ b/app/presenters/grade_summary_assignment_presenter.rb @@ -30,10 +30,12 @@ class GradeSummaryAssignmentPresenter def upload_status return unless submission + # The sort here ensures that statuses received are in the failed, + # pending and success order. With that security we can just pluck + # first one. submission.attachments. - where(workflow_state: ['errored', 'pending_upload']). - order(:workflow_state). - pluck(:workflow_state). + map { |a| AttachmentUploadStatus.upload_status(a) }. + sort. first end diff --git a/app/views/assignments/_submission_sidebar.html.erb b/app/views/assignments/_submission_sidebar.html.erb index fad0b52e336..223fa3cba16 100644 --- a/app/views/assignments/_submission_sidebar.html.erb +++ b/app/views/assignments/_submission_sidebar.html.erb @@ -72,7 +72,7 @@ <% js_bundle 'progress_pill' %> - + <% end %> <% elsif @current_user_submission.submission_type == "online_quiz" %> diff --git a/app/views/jst/_submission_detail.handlebars b/app/views/jst/_submission_detail.handlebars index 732665ea90d..93468104edd 100644 --- a/app/views/jst/_submission_detail.handlebars +++ b/app/views/jst/_submission_detail.handlebars @@ -13,10 +13,10 @@ {{> turnitinScore}} {{/with}} {{/if}} - {{#ifEqual workflow_state "pending_upload"}} + {{#ifEqual upload_status "pending"}} {{display_name}} {{else}} - {{#ifEqual workflow_state "errored" }} + {{#ifEqual upload_status "failed" }} {{display_name}} {{else}} {{display_name}} diff --git a/lib/api/v1/attachment.rb b/lib/api/v1/attachment.rb index 002865de61f..e4b8805bafd 100644 --- a/lib/api/v1/attachment.rb +++ b/lib/api/v1/attachment.rb @@ -43,7 +43,8 @@ module Api::V1::Attachment 'folder_id' => attachment.folder_id, 'display_name' => attachment.display_name, 'filename' => attachment.filename, - 'workflow_state' => attachment.workflow_state + 'workflow_state' => attachment.workflow_state, + 'upload_status' => AttachmentUploadStatus.upload_status(attachment) } return hash if options[:only] && options[:only].include?('names') diff --git a/public/javascripts/speed_grader.js b/public/javascripts/speed_grader.js index 93a5add2ad7..bf855dbcb85 100644 --- a/public/javascripts/speed_grader.js +++ b/public/javascripts/speed_grader.js @@ -39,8 +39,6 @@ import Tooltip from '@instructure/ui-overlays/lib/components/Tooltip' import IconUpload from '@instructure/ui-icons/lib/Line/IconUpload' import IconWarning from '@instructure/ui-icons/lib/Line/IconWarning' import IconCheckMarkIndeterminate from '@instructure/ui-icons/lib/Line/IconCheckMarkIndeterminate' -import FailedUploadTreeKite from 'jsx/speed_grader/FailedUploadTreeKite' -import WaitingWristWatch from 'jsx/speed_grader/WaitingWristWatch' import View from '@instructure/ui-layout/lib/components/View' import Text from '@instructure/ui-elements/lib/components/Text' import round from 'compiled/util/round' @@ -679,14 +677,14 @@ function unmountCommentTextArea() { function renderProgressIcon(attachment) { const mountPoint = document.getElementById('react_pill_container') let icon = [] - switch (attachment.workflow_state) { - case 'pending_upload': + switch (attachment.upload_status) { + case 'pending': icon = [, I18n.t('Uploading Submission')] break - case 'errored': + case 'failed': icon = [, I18n.t('Submission Failed to Submit')] break - case 'processed': + case 'success': break default: icon = [, I18n.t('No File Submitted')] @@ -2007,7 +2005,7 @@ EG = { [anonymizableSubmissionIdKey]: submission[anonymizableUserId], attachmentId: attachment.id, display_name: attachment.display_name, - attachmentWorkflow: attachment.workflow_state + attachmentWorkflow: attachment.upload_status }, hrefValues: [anonymizableSubmissionIdKey, 'attachmentId'] }) @@ -2280,22 +2278,6 @@ EG = { } }, - progressSubmissionPreview(attachment) { - if (attachment === undefined) { - return [ - , - I18n.t('Upload Failed'), - I18n.t('Please have the student submit the file again') - ] - } else { - return [ - , - I18n.t('Uploading'), - I18n.t('Canvas is attempting to retreive the submissions. Please check back again later.') - ] - } - }, - loadSubmissionPreview(attachment, submission) { clearInterval(sessionTimer) $submissions_container.children().hide() @@ -2322,38 +2304,11 @@ EG = { ENV.lti_retrieve_url, submission.external_tool_url || submission.url ) - } else if (this.canDisplaySpeedGraderImagePreview(jsonData.context, attachment, submission)) { - this.emptyIframeHolder() - const mountPoint = document.getElementById('iframe_holder') - mountPoint.style = '' - const state = this.progressSubmissionPreview(attachment) - ReactDOM.render( - - {state[0]} - - {state[1]} - - - {state[2]} - - , - mountPoint - ) } else { this.renderSubmissionPreview() } }, - canDisplaySpeedGraderImagePreview(context, attachment, submission) { - const type = submission.submission_type - return ( - !context.quiz && - (type !== 'online_text_entry' && type !== 'media_recording' && type !== 'online_url') && - attachment === undefined && - (submission !== undefined || attachment.workflow_state === 'pending_upload') - ) - }, - emptyIframeHolder(elem) { elem = elem || $iframe_holder elem.empty() diff --git a/spec/apis/file_uploads_spec_helper.rb b/spec/apis/file_uploads_spec_helper.rb index 421d818b7f4..1579d57e75b 100644 --- a/spec/apis/file_uploads_spec_helper.rb +++ b/spec/apis/file_uploads_spec_helper.rb @@ -49,6 +49,7 @@ shared_examples_for "file uploads api" do 'display_name' => attachment.display_name, 'filename' => attachment.filename, 'workflow_state' => "processed", + 'upload_status' => "success", 'size' => attachment.size, 'unlock_at' => attachment.unlock_at ? attachment.unlock_at.as_json : nil, 'locked' => !!attachment.locked, @@ -124,6 +125,7 @@ shared_examples_for "file uploads api" do 'hidden_for_user' => false, 'created_at' => attachment.created_at.as_json, 'updated_at' => attachment.updated_at.as_json, + 'upload_status' => "success", 'thumbnail_url' => attachment.has_thumbnail? ? thumbnail_image_url(attachment, attachment.uuid, host: 'www.example.com') : nil, 'modified_at' => attachment.modified_at.as_json, 'mime_class' => attachment.mime_class, diff --git a/spec/apis/lti/submissions_api_spec.rb b/spec/apis/lti/submissions_api_spec.rb index 9a9796fccc5..f82d81ee3d0 100644 --- a/spec/apis/lti/submissions_api_spec.rb +++ b/spec/apis/lti/submissions_api_spec.rb @@ -144,6 +144,7 @@ module Lti "filename" => attachment.filename, "display_name" => attachment.display_name, "created_at" => now.iso8601, + "upload_status" => "success", "updated_at" => now.iso8601 } ] @@ -200,6 +201,7 @@ module Lti "filename" => attachment.filename, "display_name" => attachment.display_name, "created_at" => now.iso8601, + "upload_status" => "success", "updated_at" => now.iso8601 } ] diff --git a/spec/apis/v1/conversations_api_spec.rb b/spec/apis/v1/conversations_api_spec.rb index f949b2cd0dd..f3015636087 100644 --- a/spec/apis/v1/conversations_api_spec.rb +++ b/spec/apis/v1/conversations_api_spec.rb @@ -1003,6 +1003,7 @@ describe ConversationsController, type: :request do 'hidden_for_user' => false, 'created_at' => attachment.created_at.as_json, 'updated_at' => attachment.updated_at.as_json, + 'upload_status' => "success", 'modified_at' => attachment.modified_at.as_json, 'thumbnail_url' => thumbnail_image_url(attachment, attachment.uuid, host: 'www.example.com'), 'mime_class' => attachment.mime_class, @@ -1247,6 +1248,7 @@ describe ConversationsController, type: :request do 'hidden_for_user' => false, 'created_at' => attachment.created_at.as_json, 'updated_at' => attachment.updated_at.as_json, + 'upload_status' => "success", 'thumbnail_url' => nil, 'modified_at' => attachment.modified_at.as_json, 'mime_class' => attachment.mime_class, diff --git a/spec/apis/v1/discussion_topics_api_spec.rb b/spec/apis/v1/discussion_topics_api_spec.rb index d45ee4aea28..002286ba81e 100644 --- a/spec/apis/v1/discussion_topics_api_spec.rb +++ b/spec/apis/v1/discussion_topics_api_spec.rb @@ -391,6 +391,7 @@ describe DiscussionTopicsController, type: :request do 'hidden_for_user' => false, 'created_at' => @attachment.created_at.as_json, 'updated_at' => @attachment.updated_at.as_json, + 'upload_status' => "success", 'modified_at' => @attachment.modified_at.as_json, 'thumbnail_url' => nil, 'mime_class' => @attachment.mime_class, @@ -1518,6 +1519,7 @@ describe DiscussionTopicsController, type: :request do 'hidden_for_user' => false, 'created_at' => attachment.created_at.as_json, 'updated_at' => attachment.updated_at.as_json, + 'upload_status' => "success", 'thumbnail_url' => nil, 'modified_at' => attachment.modified_at.as_json, 'mime_class' => attachment.mime_class, @@ -2598,6 +2600,7 @@ describe DiscussionTopicsController, type: :request do 'hidden_for_user' => false, 'created_at' => @attachment.created_at.as_json, 'updated_at' => @attachment.updated_at.as_json, + 'upload_status' => "success", 'thumbnail_url' => nil, 'modified_at' => @attachment.modified_at.as_json, 'mime_class' => @attachment.mime_class, diff --git a/spec/apis/v1/files_controller_api_spec.rb b/spec/apis/v1/files_controller_api_spec.rb index 4b022361ab8..2108a2857d0 100644 --- a/spec/apis/v1/files_controller_api_spec.rb +++ b/spec/apis/v1/files_controller_api_spec.rb @@ -162,6 +162,7 @@ describe "Files API", type: :request do 'hidden_for_user' => false, 'created_at' => @attachment.created_at.as_json, 'updated_at' => @attachment.updated_at.as_json, + 'upload_status' => "success", 'thumbnail_url' => nil, 'modified_at' => @attachment.modified_at.as_json, 'mime_class' => @attachment.mime_class, @@ -201,6 +202,7 @@ describe "Files API", type: :request do 'hidden_for_user' => false, 'created_at' => @attachment.created_at.as_json, 'updated_at' => @attachment.updated_at.as_json, + 'upload_status' => "success", 'thumbnail_url' => nil, 'modified_at' => @attachment.modified_at.as_json, 'mime_class' => @attachment.mime_class, @@ -832,6 +834,7 @@ describe "Files API", type: :request do 'hidden_for_user' => false, 'created_at' => @att.created_at.as_json, 'updated_at' => @att.updated_at.as_json, + 'upload_status' => "success", 'thumbnail_url' => thumbnail_image_url(@att, @att.uuid, host: 'www.example.com'), 'modified_at' => @att.modified_at.as_json, 'mime_class' => @att.mime_class, diff --git a/spec/apis/v1/submissions_api_spec.rb b/spec/apis/v1/submissions_api_spec.rb index 0040ca30db9..c22e6dee09a 100644 --- a/spec/apis/v1/submissions_api_spec.rb +++ b/spec/apis/v1/submissions_api_spec.rb @@ -1095,6 +1095,7 @@ describe 'Submissions API', type: :request do "filename" => "unknown.loser", "display_name" => "unknown.loser", "workflow_state" => "pending_upload", + "upload_status" => "success", "id" => sub1.attachments.first.id, "uuid" => sub1.attachments.first.uuid, "folder_id" => sub1.attachments.first.folder_id, @@ -1192,6 +1193,7 @@ describe 'Submissions API', type: :request do "filename" => "unknown.loser", "display_name" => "unknown.loser", "workflow_state" => "pending_upload", + "upload_status" => "success", "id" => sub1.attachments.first.id, "uuid" => sub1.attachments.first.uuid, "folder_id" => sub1.attachments.first.folder_id, @@ -1304,6 +1306,7 @@ describe 'Submissions API', type: :request do "display_name" => "snapshot.png", "filename" => "snapshot.png", "workflow_state" => "pending_upload", + "upload_status" => "success", "url" => "http://www.example.com/files/#{sub2a1.id}/download?download_frd=1&verifier=#{sub2a1.uuid}", "id" => sub2a1.id, "uuid" => sub2a1.uuid, @@ -1342,6 +1345,7 @@ describe 'Submissions API', type: :request do "display_name" => "snapshot.png", "filename" => "snapshot.png", "workflow_state" => "pending_upload", + "upload_status" => "success", "url" => "http://www.example.com/files/#{sub2a1.id}/download?download_frd=1&verifier=#{sub2a1.uuid}", "id" => sub2a1.id, "uuid" => sub2a1.uuid, diff --git a/spec/coffeescripts/gradebook/SubmissionCellSpec.js b/spec/coffeescripts/gradebook/SubmissionCellSpec.js index b4ca04a7a57..c6472ddbc77 100644 --- a/spec/coffeescripts/gradebook/SubmissionCellSpec.js +++ b/spec/coffeescripts/gradebook/SubmissionCellSpec.js @@ -554,6 +554,28 @@ test('#loadValue sets the value to grade when entered_grade is not available', f strictEqual(this.cell.val, 'complete') }) +QUnit.module('SubmissionCell#submissionIcon', () => { + test('returns icon class for an attachment with upload_status of pending', () => { + const attachments = [{upload_status: 'pending'}] + strictEqual( + SubmissionCell.submissionIcon('default', attachments), + "" + ) + }) + + test('returns icon class for an attachment with upload_status of failed', () => { + const attachments = [{upload_status: 'failed'}] + strictEqual( + SubmissionCell.submissionIcon('default', attachments), + "" + ) + }) + + test('returns icon class of document for an attachment with no attachments', () => { + strictEqual(SubmissionCell.submissionIcon('document'), "") + }) +}) + QUnit.module('SubmissionCell#classesBasedOnSubmission', () => { test('returns anonymous when anonymize_students is set on the assignment', () => { const assignment = {anonymize_students: true} diff --git a/spec/javascripts/jsx/speed_graderSpec.js b/spec/javascripts/jsx/speed_graderSpec.js index fab05d74b7e..c27ec5faa8d 100644 --- a/spec/javascripts/jsx/speed_graderSpec.js +++ b/spec/javascripts/jsx/speed_graderSpec.js @@ -2187,6 +2187,135 @@ QUnit.module('SpeedGrader', function(suiteHooks) { }) }) + QUnit.module('#renderProgressIcon', function(hooks) { + const assignment = {} + const student = { + id: '1', + submission_history: [] + } + const enrollment = {user_id: student.id, course_section_id: '1'} + const submissionComment = { + created_at: new Date().toISOString(), + publishable: false, + comment: 'a comment', + author_id: 1, + author_name: 'an author' + } + const submission = { + id: '3', + user_id: '1', + grade_matches_current_submission: true, + workflow_state: 'active', + submitted_at: new Date().toISOString(), + grade: 'A', + assignment_id: '456', + submission_comments: [submissionComment] + } + const windowJsonData = { + ...assignment, + context_id: '123', + context: { + students: [student], + enrollments: [enrollment], + active_course_sections: [], + rep_for_student: {} + }, + submissions: [submission], + gradingPeriods: [] + } + + let jsonData + let commentToRender + + const commentBlankHtml = ` +
+ + + + Delete comment + +
+
+ ` + + const commentAttachmentBlank = ` +
+   +
+ ` + + hooks.beforeEach(() => { + ;({jsonData} = window) + fakeENV.setup({ + ...ENV, + assignment_id: '17', + course_id: '29', + grading_role: 'moderator', + help_url: 'example.com/support', + show_help_menu_item: false, + current_user_id: '1', + RUBRIC_ASSESSMENT: {} + }) + + setupFixtures(` +
+ `) + SpeedGrader.setup() + window.jsonData = windowJsonData + SpeedGrader.EG.jsonReady() + setupCurrentStudent() + commentToRender = {...submissionComment} + commentToRender.draft = true + + commentRenderingOptions = { + commentBlank: $(commentBlankHtml), + commentAttachmentBlank: $(commentAttachmentBlank) + } + }) + + hooks.afterEach(() => { + teardownFixtures() + delete SpeedGrader.EG.currentStudent + window.jsonData = jsonData + SpeedGrader.teardown() + document.querySelector('.ui-selectmenu-menu').remove() + }) + + + test('mounts the progressIcon when attachment uplod_status is pending', function() { + const attachment = {content_type: 'application/rtf', upload_status: 'pending'} + SpeedGrader.EG.renderAttachment(attachment) + + strictEqual( + document.getElementById('react_pill_container').children.length, + 0 + ) + }) + + test('mounts the progressIcon when attachment uplod_status is failed', function() { + const attachment = {content_type: 'application/rtf', upload_status: 'failed'} + SpeedGrader.EG.renderAttachment(attachment) + + strictEqual( + document.getElementById('react_pill_container').children.length, + 0 + ) + }) + + test('mounts the file name preview when attachment uplod_status is success', function() { + const attachment = {content_type: 'application/rtf', upload_status: 'success'} + SpeedGrader.EG.renderAttachment(attachment) + + strictEqual( + document.getElementById('react_pill_container').children.length, + 0 + ) + }) + }) + + QUnit.module('#renderCommentTextArea', function(hooks) { hooks.beforeEach(function() { setupFixtures('
') diff --git a/spec/presenters/grade_summary_assignment_presenter_spec.rb b/spec/presenters/grade_summary_assignment_presenter_spec.rb index 4fe5d486374..25d4ca8d93c 100644 --- a/spec/presenters/grade_summary_assignment_presenter_spec.rb +++ b/spec/presenters/grade_summary_assignment_presenter_spec.rb @@ -74,17 +74,17 @@ describe GradeSummaryAssignmentPresenter do end describe '#upload_status' do - it 'returns attachment workflow_state when workflow_state is pending_upload' do - expect(presenter.upload_status).to eq('pending_upload') + it 'returns attachment upload_status when upload_status is pending' do + allow(Rails.cache).to receive(:read).and_return('pending') + expect(presenter.upload_status).to eq('pending') end - it 'returns attachment workflow_state when workflow_state is errored' do - @attachment.workflow_state = 'errored' - @attachment.save! - expect(presenter.upload_status).to eq('errored') + it 'returns attachment upload_status when upload_status is failed' do + allow(Rails.cache).to receive(:read).and_return('failed') + expect(presenter.upload_status).to eq('failed') end - it 'returns the proper attachment when there are muliptle attachments in different states' do + it 'returns the proper attachment when there are multiple attachments in different states' do attachment_1 = attachment_model(context: @student) attachment_1.workflow_state = 'success' attachment_1.save! @@ -93,7 +93,10 @@ describe GradeSummaryAssignmentPresenter do attachment_2.save! attachment_3 = attachment_model(context: @student) @assignment.submit_homework @student, attachments: [attachment_1, attachment_2, attachment_3] - expect(presenter.upload_status).to eq('errored') + AttachmentUploadStatus.success!(attachment_1) + AttachmentUploadStatus.failed!(attachment_2, 'bad things') + AttachmentUploadStatus.pending!(attachment_3) + expect(presenter.upload_status).to eq('failed') end end