don't attempt to import incomplete content shares

test plan:
 - create a content share
 - immediately view the share as the receiver*
 - it should show as "Pending" and should not have an Import option
 - wait a minute and refresh the page
 - it should show a received time and be importable

*if you have trouble being fast enough, stop jobs, create the share,
 visit the page, then start jobs

flag = direct_share

fixes LS-1353

Change-Id: I6bebdb3f80deb796f3c3c46310f05edf84bd3147
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/245644
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
QA-Review: Robin Kuss <rkuss@instructure.com>
Reviewed-by: Ed Schiebel <eschiebel@instructure.com>
Product-Review: Jeremy Stanley <jeremy@instructure.com>
This commit is contained in:
Jeremy Stanley 2020-08-20 16:00:21 -06:00
parent 307e6bbaf8
commit b12ab50144
4 changed files with 74 additions and 17 deletions

View File

@ -49,6 +49,24 @@ ReceivedTable.propTypes = {
export default function ReceivedTable({shares, onPreview, onImport, onRemove, onUpdate}) {
function renderActionMenu(share) {
const items = []
if (share.content_export?.workflow_state === 'exported') {
items.push(
<Menu.Item key="prv" data-testid="preview-menu-action" onSelect={() => onPreview(share)}>
<IconEyeLine /> <View margin="0 0 0 x-small">{I18n.t('Preview')}</View>
</Menu.Item>
)
items.push(
<Menu.Item key="imp" data-testid="import-menu-action" onSelect={() => onImport(share)}>
<IconImportLine /> <View margin="0 0 0 x-small">{I18n.t('Import')}</View>
</Menu.Item>
)
}
items.push(
<Menu.Item key="rmv" data-testid="remove-menu-action" onSelect={() => onRemove(share)}>
<IconTrashLine /> <View margin="0 0 0 x-small">{I18n.t('Remove')}</View>
</Menu.Item>
)
return (
<Menu
data-testid="action-menu"
@ -60,15 +78,7 @@ export default function ReceivedTable({shares, onPreview, onImport, onRemove, on
</Button>
}
>
<Menu.Item data-testid="preview-menu-action" onSelect={() => onPreview(share)}>
<IconEyeLine /> <View margin="0 0 0 x-small">{I18n.t('Preview')}</View>
</Menu.Item>
<Menu.Item data-testid="import-menu-action" onSelect={() => onImport(share)}>
<IconImportLine /> <View margin="0 0 0 x-small">{I18n.t('Import')}</View>
</Menu.Item>
<Menu.Item data-testid="remove-menu-action" onSelect={() => onRemove(share)}>
<IconTrashLine /> <View margin="0 0 0 x-small">{I18n.t('Remove')}</View>
</Menu.Item>
{items}
</Menu>
)
}
@ -137,6 +147,28 @@ export default function ReceivedTable({shares, onPreview, onImport, onRemove, on
}
}
function renderReceivedColumn(content_export) {
if (content_export && content_export.workflow_state === 'exported') {
return <FriendlyDatetime dateTime={content_export.created_at} />
} else if (
!content_export ||
content_export.workflow_state === 'failed' ||
content_export.workflow_state === 'deleted'
) {
return (
<Text color="danger">
<em>{I18n.t('Failed')}</em>
</Text>
)
} else {
return (
<Text color="secondary">
<em>{I18n.t('Pending')}</em>
</Text>
)
}
}
function renderRow(share) {
return (
<Table.Row key={share.id}>
@ -155,9 +187,7 @@ export default function ReceivedTable({shares, onPreview, onImport, onRemove, on
/>{' '}
{share.sender.display_name}
</Table.Cell>
<Table.Cell>
<FriendlyDatetime dateTime={share.created_at} />
</Table.Cell>
<Table.Cell>{renderReceivedColumn(share.content_export)}</Table.Cell>
<Table.Cell>{renderActionMenu(share)}</Table.Cell>
</Table.Row>
)

View File

@ -20,6 +20,7 @@ import React from 'react'
import {fireEvent, render} from '@testing-library/react'
import ReceivedTable from 'jsx/content_shares/ReceivedTable'
import {
mockShare,
assignmentShare,
readDiscussionShare,
unreadDiscussionShare
@ -123,4 +124,26 @@ describe('content shares table', () => {
fireEvent.click(getByText('Remove'))
expect(onRemove).toHaveBeenCalledWith(assignmentShare)
})
it('handles a missing content_export', () => {
const brokenShare = mockShare({content_export: null})
const {getByText, queryByText, queryByTestId} = render(<ReceivedTable shares={[brokenShare]} />)
fireEvent.click(getByText(/manage options/i))
expect(queryByText('Remove')).toBeInTheDocument()
expect(queryByText('Failed')).toBeInTheDocument()
expect(queryByTestId('import-menu-action')).not.toBeInTheDocument()
expect(queryByTestId('preview-menu-action')).not.toBeInTheDocument()
})
it('handles an incomplete content_export', () => {
const pendingShare = mockShare({content_export: {id: 4, workflow_state: 'exporting'}})
const {getByText, queryByText, queryByTestId} = render(
<ReceivedTable shares={[pendingShare]} />
)
fireEvent.click(getByText(/manage options/i))
expect(queryByText('Remove')).toBeInTheDocument()
expect(queryByText('Pending')).toBeInTheDocument()
expect(queryByTestId('import-menu-action')).not.toBeInTheDocument()
expect(queryByTestId('preview-menu-action')).not.toBeInTheDocument()
})
})

View File

@ -31,8 +31,11 @@ export function mockShare(overrides = {}) {
read_state: 'read',
content_export: {
id: '3',
workflow_state: 'exported',
created_at: '2019-07-04T12:00:00Z',
attachment: {
id: '4',
created_at: '2019-07-04T12:00:00Z',
url: 'https://attachment.url'
}
},

View File

@ -37,16 +37,17 @@ describe "direct share page" do
@assignment_1 = @course_1.assignments.create!(:title => 'Assignment First', :points_possible => 10)
assignment_model(course: @course_1, name: 'assignment to share')
@course_1.root_account.enable_feature!(:direct_share)
end
before :each do
@export_1 = @course_1.content_exports.create!(settings: {"selected_content" => {"assignments" => {CC::CCHelper.create_key(@assignment_1) => '1'}}})
@export_2 = @course_1.content_exports.create!(settings: {"selected_content" => {"assignments" => {CC::CCHelper.create_key(@assignment_1) => '1'}}})
@export_3 = @course_1.content_exports.create!(settings: {"selected_content" => {"assignments" => {CC::CCHelper.create_key(@assignment_1) => '1'}}})
@export_1 = @course_1.content_exports.create!(workflow_state: 'exported', settings: {"selected_content" => {"assignments" => {CC::CCHelper.create_key(@assignment_1) => '1'}}})
@export_2 = @course_1.content_exports.create!(workflow_state: 'exported', settings: {"selected_content" => {"assignments" => {CC::CCHelper.create_key(@assignment_1) => '1'}}})
@export_3 = @course_1.content_exports.create!(workflow_state: 'exported', settings: {"selected_content" => {"assignments" => {CC::CCHelper.create_key(@assignment_1) => '1'}}})
@sent_share = @teacher_1.sent_content_shares.create! name: 'a-unread share1', content_export: @export_1, read_state: 'unread'
@unread_share1 = @teacher_2.received_content_shares.create! name: 'a-unread share1', content_export: @export_1, sender: @teacher_1, read_state: 'unread'
@unread_share2 = @teacher_2.received_content_shares.create! name: 'b-unread share2', content_export: @export_2, sender: @teacher_1, read_state: 'unread'
@read_share = @teacher_2.received_content_shares.create! name: 'c-read share', content_export: @export_3, sender: @teacher_1, read_state: 'read'
end
before :each do
user_session @teacher_2
visit_content_share_page
end