mirror of
https://github.com/nextcloud/server.git
synced 2025-09-18 15:04:08 +00:00
refactor(federatedfilesharing): Replace deprecated functions
* Replace deprecated OC dialogs methods * Replace deprecated global jQuery with axios * Replace deprecated jQuery event with event bus * Add component + unit tests for new dialog Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
This commit is contained in:
parent
9aec7b9b85
commit
55595f61df
7 changed files with 370 additions and 345 deletions
|
@ -3,3 +3,4 @@
|
|||
* SPDX-License-Identifier: CC0-1.0
|
||||
*/
|
||||
import '@testing-library/jest-dom/vitest'
|
||||
import 'core-js/stable/index.js'
|
||||
|
|
123
apps/federatedfilesharing/src/components/RemoteShareDialog.cy.ts
Normal file
123
apps/federatedfilesharing/src/components/RemoteShareDialog.cy.ts
Normal file
|
@ -0,0 +1,123 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import RemoteShareDialog from './RemoteShareDialog.vue'
|
||||
|
||||
describe('RemoteShareDialog', () => {
|
||||
it('can be mounted', () => {
|
||||
cy.mount(RemoteShareDialog, {
|
||||
propsData: {
|
||||
owner: 'user123',
|
||||
name: 'my-photos',
|
||||
remote: 'nextcloud.local',
|
||||
passwordRequired: false,
|
||||
},
|
||||
})
|
||||
|
||||
cy.findByRole('dialog')
|
||||
.should('be.visible')
|
||||
.and('contain.text', 'user123@nextcloud.local')
|
||||
.and('contain.text', 'my-photos')
|
||||
cy.findByRole('button', { name: 'Cancel' })
|
||||
.should('be.visible')
|
||||
cy.findByRole('button', { name: /add remote share/i })
|
||||
.should('be.visible')
|
||||
})
|
||||
|
||||
it('does not show password input if not enabled', () => {
|
||||
cy.mount(RemoteShareDialog, {
|
||||
propsData: {
|
||||
owner: 'user123',
|
||||
name: 'my-photos',
|
||||
remote: 'nextcloud.local',
|
||||
passwordRequired: false,
|
||||
},
|
||||
})
|
||||
|
||||
cy.findByRole('dialog')
|
||||
.should('be.visible')
|
||||
.find('input[type="password"]')
|
||||
.should('not.exist')
|
||||
})
|
||||
|
||||
it('emits true when accepted', () => {
|
||||
const onClose = cy.spy().as('onClose')
|
||||
|
||||
cy.mount(RemoteShareDialog, {
|
||||
listeners: {
|
||||
close: onClose,
|
||||
},
|
||||
propsData: {
|
||||
owner: 'user123',
|
||||
name: 'my-photos',
|
||||
remote: 'nextcloud.local',
|
||||
passwordRequired: false,
|
||||
},
|
||||
})
|
||||
|
||||
cy.findByRole('button', { name: 'Cancel' }).click()
|
||||
cy.get('@onClose')
|
||||
.should('have.been.calledWith', false)
|
||||
})
|
||||
|
||||
it('show password input if needed', () => {
|
||||
cy.mount(RemoteShareDialog, {
|
||||
propsData: {
|
||||
owner: 'admin',
|
||||
name: 'secret-data',
|
||||
remote: 'nextcloud.local',
|
||||
passwordRequired: true,
|
||||
},
|
||||
})
|
||||
|
||||
cy.findByRole('dialog')
|
||||
.should('be.visible')
|
||||
.find('input[type="password"]')
|
||||
.should('be.visible')
|
||||
})
|
||||
|
||||
it('emits the submitted password', () => {
|
||||
const onClose = cy.spy().as('onClose')
|
||||
|
||||
cy.mount(RemoteShareDialog, {
|
||||
listeners: {
|
||||
close: onClose,
|
||||
},
|
||||
propsData: {
|
||||
owner: 'admin',
|
||||
name: 'secret-data',
|
||||
remote: 'nextcloud.local',
|
||||
passwordRequired: true,
|
||||
},
|
||||
})
|
||||
|
||||
cy.get('input[type="password"]')
|
||||
.type('my password{enter}')
|
||||
cy.get('@onClose')
|
||||
.should('have.been.calledWith', true, 'my password')
|
||||
})
|
||||
|
||||
it('emits no password if cancelled', () => {
|
||||
const onClose = cy.spy().as('onClose')
|
||||
|
||||
cy.mount(RemoteShareDialog, {
|
||||
listeners: {
|
||||
close: onClose,
|
||||
},
|
||||
propsData: {
|
||||
owner: 'admin',
|
||||
name: 'secret-data',
|
||||
remote: 'nextcloud.local',
|
||||
passwordRequired: true,
|
||||
},
|
||||
})
|
||||
|
||||
cy.get('input[type="password"]')
|
||||
.type('my password')
|
||||
cy.findByRole('button', { name: 'Cancel' }).click()
|
||||
cy.get('@onClose')
|
||||
.should('have.been.calledWith', false)
|
||||
})
|
||||
})
|
|
@ -0,0 +1,67 @@
|
|||
<!--
|
||||
- SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { computed, ref } from 'vue'
|
||||
import NcDialog from '@nextcloud/vue/dist/Components/NcDialog.js'
|
||||
import NcPasswordField from '@nextcloud/vue/dist/Components/NcPasswordField.js'
|
||||
|
||||
const props = defineProps<{
|
||||
/** Name of the share */
|
||||
name: string
|
||||
/** Display name of the owner */
|
||||
owner: string
|
||||
/** The remote instance name */
|
||||
remote: string
|
||||
/** True if the user should enter a password */
|
||||
passwordRequired: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'close', state: boolean, password?: string): void
|
||||
}>()
|
||||
|
||||
const password = ref('')
|
||||
|
||||
/**
|
||||
* The dialog buttons
|
||||
*/
|
||||
const buttons = computed(() => [
|
||||
{
|
||||
label: t('federatedfilesharing', 'Cancel'),
|
||||
callback: () => emit('close', false),
|
||||
},
|
||||
{
|
||||
label: t('federatedfilesharing', 'Add remote share'),
|
||||
nativeType: props.passwordRequired ? 'submit' : undefined,
|
||||
type: 'primary',
|
||||
callback: () => emit('close', true, password.value),
|
||||
},
|
||||
])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NcDialog :buttons="buttons"
|
||||
:is-form="passwordRequired"
|
||||
:name="t('federatedfilesharing', 'Remote share')"
|
||||
@submit="emit('close', true, password)">
|
||||
<p>
|
||||
{{ t('federatedfilesharing', 'Do you want to add the remote share {name} from {owner}@{remote}?', { name, owner, remote }) }}
|
||||
</p>
|
||||
<NcPasswordField v-if="passwordRequired"
|
||||
class="remote-share-dialog__password"
|
||||
:label="t('federatedfilesharing', 'Remote share password')"
|
||||
:value.sync="password" />
|
||||
</NcDialog>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.remote-share-dialog {
|
||||
|
||||
&__password {
|
||||
margin-block: 1em .5em;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -3,10 +3,15 @@
|
|||
* SPDX-FileCopyrightText: 2014-2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
import axios, { isAxiosError } from '@nextcloud/axios'
|
||||
import { showError, showInfo } from '@nextcloud/dialogs'
|
||||
import { subscribe } from '@nextcloud/event-bus'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { showRemoteShareDialog } from './services/dialogService.ts'
|
||||
|
||||
window.OCA.Sharing = window.OCA.Sharing || {}
|
||||
window.OCA.Sharing = window.OCA.Sharing ?? {}
|
||||
|
||||
/**
|
||||
* Shows "add external share" dialog.
|
||||
|
@ -20,57 +25,40 @@ window.OCA.Sharing = window.OCA.Sharing || {}
|
|||
* @param {Function} callback the callback
|
||||
*/
|
||||
window.OCA.Sharing.showAddExternalDialog = function(share, passwordProtected, callback) {
|
||||
const remote = share.remote
|
||||
const owner = share.ownerDisplayName || share.owner
|
||||
const name = share.name
|
||||
|
||||
// Clean up the remote URL for display
|
||||
const remoteClean = remote
|
||||
const remote = share.remote
|
||||
.replace(/^https?:\/\//, '') // remove http:// or https://
|
||||
.replace(/\/$/, '') // remove trailing slash
|
||||
|
||||
if (!passwordProtected) {
|
||||
window.OC.dialogs.confirm(
|
||||
t(
|
||||
'files_sharing',
|
||||
'Do you want to add the remote share {name} from {owner}@{remote}?',
|
||||
{ name, owner, remote: remoteClean },
|
||||
),
|
||||
t('files_sharing', 'Remote share'),
|
||||
function(result) {
|
||||
callback(result, share)
|
||||
},
|
||||
true,
|
||||
).then(this._adjustDialog)
|
||||
} else {
|
||||
window.OC.dialogs.prompt(
|
||||
t(
|
||||
'files_sharing',
|
||||
'Do you want to add the remote share {name} from {owner}@{remote}?',
|
||||
{ name, owner, remote: remoteClean },
|
||||
),
|
||||
t('files_sharing', 'Remote share'),
|
||||
function(result, password) {
|
||||
share.password = password
|
||||
callback(result, share)
|
||||
},
|
||||
true,
|
||||
t('files_sharing', 'Remote share password'),
|
||||
true,
|
||||
).then(this._adjustDialog)
|
||||
showRemoteShareDialog(name, owner, remote, passwordProtected)
|
||||
.then((result, password) => callback(result, { ...share, password }))
|
||||
// eslint-disable-next-line n/no-callback-literal
|
||||
.catch(() => callback(false, share))
|
||||
}
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
processIncomingShareFromUrl()
|
||||
|
||||
if (loadState('federatedfilesharing', 'notificationsEnabled', true) !== true) {
|
||||
// No notification app, display the modal
|
||||
processSharesToConfirm()
|
||||
}
|
||||
}
|
||||
|
||||
window.OCA.Sharing._adjustDialog = function() {
|
||||
const $dialog = $('.oc-dialog:visible')
|
||||
const $buttons = $dialog.find('button')
|
||||
// hack the buttons
|
||||
$dialog.find('.ui-icon').remove()
|
||||
$buttons.eq(1).text(t('core', 'Cancel'))
|
||||
$buttons.eq(2).text(t('files_sharing', 'Add remote share'))
|
||||
}
|
||||
subscribe('notifications:action:executed', ({ action, notification }) => {
|
||||
if (notification.app === 'files_sharing' && notification.object_type === 'remote_share' && action.type === 'POST') {
|
||||
// User accepted a remote share reload
|
||||
reloadFilesList()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const reloadFilesList = function() {
|
||||
/**
|
||||
* Reload the files list to show accepted shares
|
||||
*/
|
||||
function reloadFilesList() {
|
||||
if (!window?.OCP?.Files?.Router?.goToRoute) {
|
||||
// No router, just reload the page
|
||||
window.location.reload()
|
||||
|
@ -89,35 +77,42 @@ const reloadFilesList = function() {
|
|||
* Process incoming remote share that might have been passed
|
||||
* through the URL
|
||||
*/
|
||||
const processIncomingShareFromUrl = function() {
|
||||
function processIncomingShareFromUrl() {
|
||||
const params = window.OC.Util.History.parseUrlQuery()
|
||||
|
||||
// manually add server-to-server share
|
||||
if (params.remote && params.token && params.name) {
|
||||
|
||||
const callbackAddShare = function(result, share) {
|
||||
const password = share.password || ''
|
||||
if (result) {
|
||||
$.post(
|
||||
generateUrl('apps/federatedfilesharing/askForFederatedShare'),
|
||||
{
|
||||
remote: share.remote,
|
||||
token: share.token,
|
||||
owner: share.owner,
|
||||
ownerDisplayName: share.ownerDisplayName || share.owner,
|
||||
name: share.name,
|
||||
password,
|
||||
},
|
||||
).done(function(data) {
|
||||
if (Object.hasOwn(data, 'legacyMount')) {
|
||||
reloadFilesList()
|
||||
} else {
|
||||
window.OC.Notification.showTemporary(data.message)
|
||||
}
|
||||
}).fail(function(data) {
|
||||
window.OC.Notification.showTemporary(JSON.parse(data.responseText).message)
|
||||
})
|
||||
const callbackAddShare = (result, share) => {
|
||||
if (result === false) {
|
||||
return
|
||||
}
|
||||
|
||||
axios.post(
|
||||
generateUrl('apps/federatedfilesharing/askForFederatedShare'),
|
||||
{
|
||||
remote: share.remote,
|
||||
token: share.token,
|
||||
owner: share.owner,
|
||||
ownerDisplayName: share.ownerDisplayName || share.owner,
|
||||
name: share.name,
|
||||
password: share.password || '',
|
||||
},
|
||||
).then(({ data }) => {
|
||||
if (Object.hasOwn(data, 'legacyMount')) {
|
||||
reloadFilesList()
|
||||
} else {
|
||||
showInfo(data.message)
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.error('Error while processing incoming share', error)
|
||||
|
||||
if (isAxiosError(error) && error.response.data.message) {
|
||||
showError(error.response.data.message)
|
||||
} else {
|
||||
showError(t('federatedfilesharing', 'Incoming share could not be processed'))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// clear hash, it is unlikely that it contain any extra parameters
|
||||
|
@ -134,44 +129,23 @@ const processIncomingShareFromUrl = function() {
|
|||
/**
|
||||
* Retrieve a list of remote shares that need to be approved
|
||||
*/
|
||||
const processSharesToConfirm = function() {
|
||||
async function processSharesToConfirm() {
|
||||
// check for new server-to-server shares which need to be approved
|
||||
$.get(generateUrl('/apps/files_sharing/api/externalShares'), {}, function(shares) {
|
||||
let index
|
||||
for (index = 0; index < shares.length; ++index) {
|
||||
window.OCA.Sharing.showAddExternalDialog(
|
||||
shares[index],
|
||||
false,
|
||||
function(result, share) {
|
||||
if (result) {
|
||||
// Accept
|
||||
$.post(generateUrl('/apps/files_sharing/api/externalShares'), { id: share.id })
|
||||
.then(function() {
|
||||
reloadFilesList()
|
||||
})
|
||||
} else {
|
||||
// Delete
|
||||
$.ajax({
|
||||
url: generateUrl('/apps/files_sharing/api/externalShares/' + share.id),
|
||||
type: 'DELETE',
|
||||
})
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
processIncomingShareFromUrl()
|
||||
|
||||
if (loadState('federatedfilesharing', 'notificationsEnabled', true) !== true) {
|
||||
// No notification app, display the modal
|
||||
processSharesToConfirm()
|
||||
}
|
||||
|
||||
$('body').on('window.OCA.Notification.Action', function(e) {
|
||||
if (e.notification.app === 'files_sharing' && e.notification.object_type === 'remote_share' && e.action.type === 'POST') {
|
||||
// User accepted a remote share reload
|
||||
reloadFilesList()
|
||||
const { data: shares } = await axios.get(generateUrl('/apps/files_sharing/api/externalShares'))
|
||||
for (let index = 0; index < shares.length; ++index) {
|
||||
window.OCA.Sharing.showAddExternalDialog(
|
||||
shares[index],
|
||||
false,
|
||||
function(result, share) {
|
||||
if (result) {
|
||||
// Accept
|
||||
axios.post(generateUrl('/apps/files_sharing/api/externalShares'), { id: share.id })
|
||||
.then(() => reloadFilesList())
|
||||
} else {
|
||||
// Delete
|
||||
axios.delete(generateUrl('/apps/files_sharing/api/externalShares/' + share.id))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
65
apps/federatedfilesharing/src/services/dialogService.spec.ts
Normal file
65
apps/federatedfilesharing/src/services/dialogService.spec.ts
Normal file
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { showRemoteShareDialog } from './dialogService'
|
||||
import { nextTick } from 'vue'
|
||||
|
||||
describe('federatedfilesharing: dialog service', () => {
|
||||
it('mounts dialog', async () => {
|
||||
showRemoteShareDialog('share-name', 'user123', 'example.com')
|
||||
await nextTick()
|
||||
expect(document.querySelector('[role="dialog"]')).not.toBeNull()
|
||||
expect(document.querySelector('[role="dialog"]')!.textContent).to.contain('share-name')
|
||||
expect(document.querySelector('[role="dialog"]')!.textContent).to.contain('user123@example.com')
|
||||
expect(document.querySelector('[role="dialog"] input[type="password"]')).toBeNull()
|
||||
})
|
||||
|
||||
it('shows password input', async () => {
|
||||
showRemoteShareDialog('share-name', 'user123', 'example.com', true)
|
||||
await nextTick()
|
||||
expect(document.querySelector('[role="dialog"]')).not.toBeNull()
|
||||
expect(document.querySelector('[role="dialog"] input[type="password"]')).not.toBeNull()
|
||||
})
|
||||
|
||||
it('resolves if accepted', async () => {
|
||||
const promise = showRemoteShareDialog('share-name', 'user123', 'example.com')
|
||||
await nextTick()
|
||||
|
||||
for (const button of document.querySelectorAll('button').values()) {
|
||||
if (button.textContent?.match(/add remote share/i)) {
|
||||
button.click()
|
||||
}
|
||||
}
|
||||
|
||||
expect(await promise).toBe(undefined)
|
||||
})
|
||||
|
||||
it('resolves password if accepted', async () => {
|
||||
const promise = showRemoteShareDialog('share-name', 'user123', 'example.com', true)
|
||||
await nextTick()
|
||||
|
||||
for (const button of document.querySelectorAll('button').values()) {
|
||||
if (button.textContent?.match(/add remote share/i)) {
|
||||
button.click()
|
||||
}
|
||||
}
|
||||
|
||||
expect(await promise).toBe('')
|
||||
})
|
||||
|
||||
it('rejects if cancelled', async () => {
|
||||
const promise = showRemoteShareDialog('share-name', 'user123', 'example.com')
|
||||
await nextTick()
|
||||
|
||||
for (const button of document.querySelectorAll('button').values()) {
|
||||
if (button.textContent?.match(/cancel/i)) {
|
||||
button.click()
|
||||
}
|
||||
}
|
||||
|
||||
expect(async () => await promise).rejects.toThrow()
|
||||
})
|
||||
})
|
36
apps/federatedfilesharing/src/services/dialogService.ts
Normal file
36
apps/federatedfilesharing/src/services/dialogService.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { spawnDialog } from '@nextcloud/dialogs'
|
||||
import RemoteShareDialog from '../components/RemoteShareDialog.vue'
|
||||
|
||||
/**
|
||||
* Open a dialog to ask the user whether to add a remote share.
|
||||
*
|
||||
* @param name The name of the share
|
||||
* @param owner The owner of the share
|
||||
* @param remote The remote address
|
||||
* @param passwordRequired True if the share is password protected
|
||||
*/
|
||||
export function showRemoteShareDialog(
|
||||
name: string,
|
||||
owner: string,
|
||||
remote: string,
|
||||
passwordRequired = false,
|
||||
): Promise<string|void> {
|
||||
const { promise, reject, resolve } = Promise.withResolvers<string|void>()
|
||||
|
||||
spawnDialog(RemoteShareDialog, { name, owner, remote, passwordRequired }, (status, password) => {
|
||||
if (passwordRequired && status) {
|
||||
resolve(password as string)
|
||||
} else if (status) {
|
||||
resolve(undefined)
|
||||
} else {
|
||||
reject()
|
||||
}
|
||||
})
|
||||
|
||||
return promise
|
||||
}
|
|
@ -1,241 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
describe('OCA.Sharing external tests', function() {
|
||||
var plugin;
|
||||
var urlQueryStub;
|
||||
var promptDialogStub;
|
||||
var confirmDialogStub;
|
||||
|
||||
function dummyShowDialog() {
|
||||
var deferred = $.Deferred();
|
||||
deferred.resolve();
|
||||
return deferred.promise();
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
plugin = OCA.Sharing.ExternalShareDialogPlugin;
|
||||
urlQueryStub = sinon.stub(OC.Util.History, 'parseUrlQuery');
|
||||
|
||||
confirmDialogStub = sinon.stub(OC.dialogs, 'confirm').callsFake(dummyShowDialog);
|
||||
promptDialogStub = sinon.stub(OC.dialogs, 'prompt').callsFake(dummyShowDialog);
|
||||
|
||||
plugin.filesApp = {
|
||||
fileList: {
|
||||
reload: sinon.stub()
|
||||
}
|
||||
};
|
||||
});
|
||||
afterEach(function() {
|
||||
urlQueryStub.restore();
|
||||
confirmDialogStub.restore();
|
||||
promptDialogStub.restore();
|
||||
plugin = null;
|
||||
});
|
||||
describe('confirmation dialog from URL', function() {
|
||||
var testShare;
|
||||
|
||||
/**
|
||||
* Checks that the server call's query matches what is
|
||||
* expected.
|
||||
*
|
||||
* @param {Object} expectedQuery expected query params
|
||||
*/
|
||||
function checkRequest(expectedQuery) {
|
||||
var request = fakeServer.requests[0];
|
||||
var query = OC.parseQueryString(request.requestBody);
|
||||
expect(request.method).toEqual('POST');
|
||||
expect(query).toEqual(expectedQuery);
|
||||
|
||||
request.respond(
|
||||
200,
|
||||
{'Content-Type': 'application/json'},
|
||||
JSON.stringify({status: 'success'})
|
||||
);
|
||||
expect(plugin.filesApp.fileList.reload.calledOnce).toEqual(true);
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
testShare = {
|
||||
remote: 'http://example.com/owncloud',
|
||||
token: 'abcdefg',
|
||||
owner: 'theowner',
|
||||
ownerDisplayName: 'The Generous Owner',
|
||||
name: 'the share name'
|
||||
};
|
||||
});
|
||||
it('does nothing when no share was passed in URL', function() {
|
||||
urlQueryStub.returns({});
|
||||
plugin.processIncomingShareFromUrl();
|
||||
expect(promptDialogStub.notCalled).toEqual(true);
|
||||
expect(confirmDialogStub.notCalled).toEqual(true);
|
||||
expect(fakeServer.requests.length).toEqual(0);
|
||||
});
|
||||
it('sends share info to server on confirm', function() {
|
||||
urlQueryStub.returns(testShare);
|
||||
plugin.processIncomingShareFromUrl();
|
||||
expect(promptDialogStub.notCalled).toEqual(true);
|
||||
expect(confirmDialogStub.calledOnce).toEqual(true);
|
||||
confirmDialogStub.getCall(0).args[2](true);
|
||||
expect(fakeServer.requests.length).toEqual(1);
|
||||
checkRequest({
|
||||
remote: 'http://example.com/owncloud',
|
||||
token: 'abcdefg',
|
||||
owner: 'theowner',
|
||||
ownerDisplayName: 'The Generous Owner',
|
||||
name: 'the share name',
|
||||
password: ''
|
||||
});
|
||||
});
|
||||
it('sends share info with password to server on confirm', function() {
|
||||
testShare = _.extend(testShare, {protected: 1});
|
||||
urlQueryStub.returns(testShare);
|
||||
plugin.processIncomingShareFromUrl();
|
||||
expect(promptDialogStub.calledOnce).toEqual(true);
|
||||
expect(confirmDialogStub.notCalled).toEqual(true);
|
||||
promptDialogStub.getCall(0).args[2](true, 'thepassword');
|
||||
expect(fakeServer.requests.length).toEqual(1);
|
||||
checkRequest({
|
||||
remote: 'http://example.com/owncloud',
|
||||
token: 'abcdefg',
|
||||
owner: 'theowner',
|
||||
ownerDisplayName: 'The Generous Owner',
|
||||
name: 'the share name',
|
||||
password: 'thepassword'
|
||||
});
|
||||
});
|
||||
it('does not send share info on cancel', function() {
|
||||
urlQueryStub.returns(testShare);
|
||||
plugin.processIncomingShareFromUrl();
|
||||
expect(promptDialogStub.notCalled).toEqual(true);
|
||||
expect(confirmDialogStub.calledOnce).toEqual(true);
|
||||
confirmDialogStub.getCall(0).args[2](false);
|
||||
expect(fakeServer.requests.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
describe('show dialog for each share to confirm', function() {
|
||||
var testShare;
|
||||
|
||||
/**
|
||||
* Call processSharesToConfirm() and make the fake server
|
||||
* return the passed response.
|
||||
*
|
||||
* @param {Array} response list of shares to process
|
||||
*/
|
||||
function processShares(response) {
|
||||
plugin.processSharesToConfirm();
|
||||
|
||||
expect(fakeServer.requests.length).toEqual(1);
|
||||
|
||||
var req = fakeServer.requests[0];
|
||||
expect(req.method).toEqual('GET');
|
||||
expect(req.url).toEqual(OC.getRootPath() + '/index.php/apps/files_sharing/api/externalShares');
|
||||
|
||||
req.respond(
|
||||
200,
|
||||
{'Content-Type': 'application/json'},
|
||||
JSON.stringify(response)
|
||||
);
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
testShare = {
|
||||
id: 123,
|
||||
remote: 'http://example.com/owncloud',
|
||||
token: 'abcdefg',
|
||||
owner: 'theowner',
|
||||
ownerDisplayName: 'The Generous Owner',
|
||||
name: 'the share name'
|
||||
};
|
||||
});
|
||||
|
||||
it('does not show any dialog if no shares to confirm', function() {
|
||||
processShares([]);
|
||||
expect(confirmDialogStub.notCalled).toEqual(true);
|
||||
expect(promptDialogStub.notCalled).toEqual(true);
|
||||
});
|
||||
it('sends accept info to server on confirm', function() {
|
||||
processShares([testShare]);
|
||||
|
||||
expect(promptDialogStub.notCalled).toEqual(true);
|
||||
expect(confirmDialogStub.calledOnce).toEqual(true);
|
||||
|
||||
confirmDialogStub.getCall(0).args[2](true);
|
||||
|
||||
expect(fakeServer.requests.length).toEqual(2);
|
||||
|
||||
var request = fakeServer.requests[1];
|
||||
var query = OC.parseQueryString(request.requestBody);
|
||||
expect(request.method).toEqual('POST');
|
||||
expect(query).toEqual({id: '123'});
|
||||
expect(request.url).toEqual(
|
||||
OC.getRootPath() + '/index.php/apps/files_sharing/api/externalShares'
|
||||
);
|
||||
|
||||
expect(plugin.filesApp.fileList.reload.notCalled).toEqual(true);
|
||||
request.respond(
|
||||
200,
|
||||
{'Content-Type': 'application/json'},
|
||||
JSON.stringify({status: 'success'})
|
||||
);
|
||||
expect(plugin.filesApp.fileList.reload.calledOnce).toEqual(true);
|
||||
});
|
||||
it('sends delete info to server on cancel', function() {
|
||||
processShares([testShare]);
|
||||
|
||||
expect(promptDialogStub.notCalled).toEqual(true);
|
||||
expect(confirmDialogStub.calledOnce).toEqual(true);
|
||||
|
||||
confirmDialogStub.getCall(0).args[2](false);
|
||||
|
||||
expect(fakeServer.requests.length).toEqual(2);
|
||||
|
||||
var request = fakeServer.requests[1];
|
||||
expect(request.method).toEqual('DELETE');
|
||||
expect(request.url).toEqual(
|
||||
OC.getRootPath() + '/index.php/apps/files_sharing/api/externalShares/123'
|
||||
);
|
||||
|
||||
expect(plugin.filesApp.fileList.reload.notCalled).toEqual(true);
|
||||
request.respond(
|
||||
200,
|
||||
{'Content-Type': 'application/json'},
|
||||
JSON.stringify({status: 'success'})
|
||||
);
|
||||
expect(plugin.filesApp.fileList.reload.notCalled).toEqual(true);
|
||||
});
|
||||
xit('shows another dialog when multiple shares need to be accepted', function() {
|
||||
// TODO: enable this test when fixing multiple dialogs issue / confirm loop
|
||||
var testShare2 = _.extend({}, testShare);
|
||||
testShare2.id = 256;
|
||||
processShares([testShare, testShare2]);
|
||||
|
||||
// confirm first one
|
||||
expect(confirmDialogStub.calledOnce).toEqual(true);
|
||||
confirmDialogStub.getCall(0).args[2](true);
|
||||
|
||||
// next dialog not shown yet
|
||||
expect(confirmDialogStub.calledOnce);
|
||||
|
||||
// respond to the first accept request
|
||||
fakeServer.requests[1].respond(
|
||||
200,
|
||||
{'Content-Type': 'application/json'},
|
||||
JSON.stringify({status: 'success'})
|
||||
);
|
||||
|
||||
// don't reload yet, there are other shares to confirm
|
||||
expect(plugin.filesApp.fileList.reload.notCalled).toEqual(true);
|
||||
|
||||
// cancel second share
|
||||
expect(confirmDialogStub.calledTwice).toEqual(true);
|
||||
confirmDialogStub.getCall(1).args[2](true);
|
||||
|
||||
// reload only called at the very end
|
||||
expect(plugin.filesApp.fileList.reload.calledOnce).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue