From f60bd51d56ccbb4d6954ae407dbf992c26319e8e Mon Sep 17 00:00:00 2001 From: =?utf8?q?Daniel=20Kuty=C5=82a?= Date: Tue, 9 Feb 2021 23:19:51 +0100 Subject: [PATCH] 17306: Added configurable options for favorite tree MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Arvados-DCO-1.1-Signed-off-by: Daniel Kutyła --- cypress/integration/favorites.spec.js | 99 ++++++++-------- cypress/support/commands.js | 110 ++++++++++-------- src/store/tree-picker/tree-picker-actions.ts | 7 +- .../favorites-tree-picker.tsx | 4 +- .../generic-projects-tree-picker.tsx | 8 +- .../projects-tree-picker.tsx | 2 +- 6 files changed, 119 insertions(+), 111 deletions(-) diff --git a/cypress/integration/favorites.spec.js b/cypress/integration/favorites.spec.js index 695812ca..a7ff4d6e 100644 --- a/cypress/integration/favorites.spec.js +++ b/cypress/integration/favorites.spec.js @@ -51,19 +51,18 @@ describe('Favorites tests', function () { }); }); - it.only('can copy collection to favorites', () => { + it('can copy collection to favorites', () => { cy.loginAs(adminUser); cy.createGroup(adminUser.token, { name: `my-shared-writable-project ${Math.floor(Math.random() * 999999)}`, group_class: 'project', - }).as('mySharedProject1').then(function () { + }).as('mySharedProject1').then(function (mySharedProject1) { cy.contains('Refresh').click(); - cy.get('main').contains(this.mySharedProject1.name).rightclick(); + cy.get('main').contains(mySharedProject1.name).rightclick(); cy.get('[data-cy=context-menu]').within(() => { cy.contains('Share').click(); }); - cy.wait(1000); cy.get('[id="select-permissions"]').as('selectPermissions'); cy.get('@selectPermissions').click(); cy.contains('Write').click(); @@ -76,13 +75,12 @@ describe('Favorites tests', function () { cy.createGroup(adminUser.token, { name: `my-shared-readonly-project ${Math.floor(Math.random() * 999999)}`, group_class: 'project', - }).as('mySharedProject2').then(function () { + }).as('mySharedProject2').then(function (mySharedProject2) { cy.contains('Refresh').click(); - cy.get('main').contains(this.mySharedProject2.name).rightclick(); + cy.get('main').contains(mySharedProject2.name).rightclick(); cy.get('[data-cy=context-menu]').within(() => { cy.contains('Share').click(); }); - cy.wait(1000); cy.get('.sharing-dialog').as('sharingDialog'); cy.get('@sharingDialog').find('input').first().type(activeUser.user.email); cy.get('[role=tooltip]').click(); @@ -94,51 +92,48 @@ describe('Favorites tests', function () { group_class: 'project', }).as('myProject1'); - cy.get('@mySharedProject1').then(function () { - cy.get('@mySharedProject2').then(function () { - cy.get('@myProject1').then(function () { - cy.createCollection(adminUser.token, { - name: `Test collection ${Math.floor(Math.random() * 999999)}`, - owner_uuid: activeUser.user.uuid, - manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n" - }) - .as('testCollection').then(function () { - cy.loginAs(activeUser); - - cy.contains('Shared with me').click(); - - cy.get('main').contains(this.mySharedProject1.name).rightclick(); - cy.get('[data-cy=context-menu]').within(() => { - cy.contains('Add to favorites').click(); - }); - - cy.get('main').contains(this.mySharedProject2.name).rightclick(); - cy.get('[data-cy=context-menu]').within(() => { - cy.contains('Add to favorites').click(); - }); - - cy.doSearch(`${activeUser.user.uuid}`); - - cy.get('main').contains(this.myProject1.name).rightclick(); - cy.get('[data-cy=context-menu]').within(() => { - cy.contains('Add to favorites').click(); - }); - - cy.contains(this.testCollection.name).rightclick(); - cy.get('[data-cy=context-menu]').within(() => { - cy.contains('Move to').click(); - }); - - cy.get('[data-cy=form-dialog]').within(function() { - cy.get('ul').last().find('i').click(); - cy.contains(this.myProject1.name); - cy.contains(this.mySharedProject1.name); - cy.get('ul').last() - .should('not.contain', this.mySharedProject2.name); - }); - }); + cy.createCollection(adminUser.token, { + name: `Test collection ${Math.floor(Math.random() * 999999)}`, + owner_uuid: activeUser.user.uuid, + manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n" + }) + .as('testCollection'); + + cy.getAll('@mySharedProject1', '@mySharedProject2', '@myProject1', '@testCollection') + .then(function ([mySharedProject1, mySharedProject2, myProject1, testCollection]) { + cy.loginAs(activeUser); + + cy.contains('Shared with me').click(); + + cy.get('main').contains(mySharedProject1.name).rightclick(); + cy.get('[data-cy=context-menu]').within(() => { + cy.contains('Add to favorites').click(); + }); + + cy.get('main').contains(mySharedProject2.name).rightclick(); + cy.get('[data-cy=context-menu]').within(() => { + cy.contains('Add to favorites').click(); + }); + + cy.doSearch(`${activeUser.user.uuid}`); + + cy.get('main').contains(myProject1.name).rightclick(); + cy.get('[data-cy=context-menu]').within(() => { + cy.contains('Add to favorites').click(); + }); + + cy.contains(testCollection.name).rightclick(); + cy.get('[data-cy=context-menu]').within(() => { + cy.contains('Move to').click(); + }); + + cy.get('[data-cy=form-dialog]').within(function () { + cy.get('ul').last().find('i').click(); + cy.contains(myProject1.name); + cy.contains(mySharedProject1.name); + cy.get('ul').last() + .should('not.contain', mySharedProject2.name); }); }); - }); }); -}) +}); \ No newline at end of file diff --git a/cypress/support/commands.js b/cypress/support/commands.js index bba04ba8..24e4b73a 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -32,17 +32,17 @@ const controllerURL = Cypress.env('controller_url'); const systemToken = Cypress.env('system_token'); Cypress.Commands.add( - "doRequest", (method='GET', path='', data=null, qs=null, - token=systemToken, auth=false, followRedirect=true) => { - return cy.request({ - method: method, - url: `${controllerURL}/${path}`, - body: data, - qs: auth ? qs : Object.assign({api_token: token}, qs), - auth: auth ? {bearer: `${token}`} : undefined, - followRedirect: followRedirect - }) - } + "doRequest", (method = 'GET', path = '', data = null, qs = null, + token = systemToken, auth = false, followRedirect = true) => { + return cy.request({ + method: method, + url: `${controllerURL}/${path}`, + body: data, + qs: auth ? qs : Object.assign({ api_token: token }, qs), + auth: auth ? { bearer: `${token}` } : undefined, + followRedirect: followRedirect + }) +} ) // This resets the DB removing all content and seeding it with the fixtures. @@ -54,7 +54,7 @@ Cypress.Commands.add( ) Cypress.Commands.add( - "getUser", (username, first_name='', last_name='', is_admin=false, is_active=true) => { + "getUser", (username, first_name = '', last_name = '', is_admin = false, is_active = true) => { // Create user if not already created return cy.doRequest('POST', '/auth/controller/callback', { auth_info: JSON.stringify({ @@ -66,30 +66,30 @@ Cypress.Commands.add( }), return_to: ',https://example.local' }, null, systemToken, true, false) // Don't follow redirects so we can catch the token - .its('headers.location').as('location') - // Get its token and set the account up as admin and/or active - .then(function() { - this.userToken = this.location.split("=")[1] - assert.isString(this.userToken) - return cy.doRequest('GET', '/arvados/v1/users', null, { - filters: `[["username", "=", "${username}"]]` - }) - .its('body.items.0') - .as('aUser') - .then(function() { - cy.doRequest('PUT', `/arvados/v1/users/${this.aUser.uuid}`, { - user: { - is_admin: is_admin, - is_active: is_active - } - }) - .its('body') - .as('theUser') - .then(function() { - return {user: this.theUser, token: this.userToken}; + .its('headers.location').as('location') + // Get its token and set the account up as admin and/or active + .then(function () { + this.userToken = this.location.split("=")[1] + assert.isString(this.userToken) + return cy.doRequest('GET', '/arvados/v1/users', null, { + filters: `[["username", "=", "${username}"]]` }) + .its('body.items.0') + .as('aUser') + .then(function () { + cy.doRequest('PUT', `/arvados/v1/users/${this.aUser.uuid}`, { + user: { + is_admin: is_admin, + is_active: is_active + } + }) + .its('body') + .as('theUser') + .then(function () { + return { user: this.theUser, token: this.userToken }; + }) + }) }) - }) } ) @@ -145,31 +145,31 @@ Cypress.Commands.add( Cypress.Commands.add( "createResource", (token, suffix, data) => { - return cy.doRequest('POST', '/arvados/v1/'+suffix, data, null, token, true) - .its('body').as('resource') - .then(function() { - return this.resource; - }) + return cy.doRequest('POST', '/arvados/v1/' + suffix, data, null, token, true) + .its('body').as('resource') + .then(function () { + return this.resource; + }) } ) Cypress.Commands.add( "deleteResource", (token, suffix, uuid) => { - return cy.doRequest('DELETE', '/arvados/v1/'+suffix+'/'+uuid) - .its('body').as('resource') - .then(function() { - return this.resource; - }) + return cy.doRequest('DELETE', '/arvados/v1/' + suffix + '/' + uuid) + .its('body').as('resource') + .then(function () { + return this.resource; + }) } ) Cypress.Commands.add( "updateResource", (token, suffix, uuid, data) => { - return cy.doRequest('PUT', '/arvados/v1/'+suffix+'/'+uuid, data, null, token, true) - .its('body').as('resource') - .then(function() { - return this.resource; - }) + return cy.doRequest('PUT', '/arvados/v1/' + suffix + '/' + uuid, data, null, token, true) + .its('body').as('resource') + .then(function () { + return this.resource; + }) } ) @@ -186,4 +186,14 @@ Cypress.Commands.add( "doSearch", (searchTerm) => { cy.get('[data-cy=searchbar-input-field]').type(`{selectall}${searchTerm}{enter}`); } -) \ No newline at end of file +) + +Cypress.Commands.add('getAll', (...elements) => { + const promise = cy.wrap([], { log: false }) + + for (let element of elements) { + promise.then(arr => cy.get(element).then(got => cy.wrap([...arr, got]))) + } + + return promise +}) \ No newline at end of file diff --git a/src/store/tree-picker/tree-picker-actions.ts b/src/store/tree-picker/tree-picker-actions.ts index 2d9ec32d..d11f7527 100644 --- a/src/store/tree-picker/tree-picker-actions.ts +++ b/src/store/tree-picker/tree-picker-actions.ts @@ -237,7 +237,8 @@ interface LoadFavoritesProjectParams { includeFiles?: boolean; } -export const loadFavoritesProject = (params: LoadFavoritesProjectParams) => +export const loadFavoritesProject = (params: LoadFavoritesProjectParams, + options: { showOnlyOwned: boolean, showOnlyWritable: boolean } = { showOnlyOwned: true, showOnlyWritable: false }) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { const { pickerId, includeCollections = false, includeFiles = false } = params; const uuid = getUserUuid(getState()); @@ -249,13 +250,13 @@ export const loadFavoritesProject = (params: LoadFavoritesProjectParams) => fb => fb.getFilters(), )(new FilterBuilder()); - const { items } = await services.favoriteService.list(uuid, { filters }, false); + const { items } = await services.favoriteService.list(uuid, { filters }, options.showOnlyOwned); dispatch(receiveTreePickerData({ id: 'Favorites', pickerId, data: items.filter((item) => { - if ((item as GroupResource).writableBy && (item as GroupResource).writableBy.indexOf(uuid) === -1) { + if (options.showOnlyWritable && (item as GroupResource).writableBy && (item as GroupResource).writableBy.indexOf(uuid) === -1) { return false; } diff --git a/src/views-components/projects-tree-picker/favorites-tree-picker.tsx b/src/views-components/projects-tree-picker/favorites-tree-picker.tsx index 09704066..2f149f4a 100644 --- a/src/views-components/projects-tree-picker/favorites-tree-picker.tsx +++ b/src/views-components/projects-tree-picker/favorites-tree-picker.tsx @@ -11,7 +11,7 @@ import { loadFavoritesProject } from '~/store/tree-picker/tree-picker-actions'; export const FavoritesTreePicker = connect(() => ({ rootItemIcon: FavoriteIcon, }), (dispatch: Dispatch): Pick => ({ - loadRootItem: (_, pickerId, includeCollections, includeFiles) => { - dispatch(loadFavoritesProject({ pickerId, includeCollections, includeFiles })); + loadRootItem: (_, pickerId, includeCollections, includeFiles, options) => { + dispatch(loadFavoritesProject({ pickerId, includeCollections, includeFiles }, options)); }, }))(ProjectsTreePicker); \ No newline at end of file diff --git a/src/views-components/projects-tree-picker/generic-projects-tree-picker.tsx b/src/views-components/projects-tree-picker/generic-projects-tree-picker.tsx index 07b1ad81..eb756d72 100644 --- a/src/views-components/projects-tree-picker/generic-projects-tree-picker.tsx +++ b/src/views-components/projects-tree-picker/generic-projects-tree-picker.tsx @@ -33,7 +33,9 @@ export interface ProjectsTreePickerDataProps { showSelection?: boolean; relatedTreePickers?: string[]; disableActivation?: string[]; - loadRootItem: (item: TreeItem, pickerId: string, includeCollections?: boolean, includeFiles?: boolean) => void; + options?: { showOnlyOwned: boolean, showOnlyWritable: boolean }; + loadRootItem: (item: TreeItem, pickerId: string, + includeCollections?: boolean, includeFiles?: boolean, options?: { showOnlyOwned: boolean, showOnlyWritable: boolean }) => void; } export type ProjectsTreePickerProps = ProjectsTreePickerDataProps & Partial; @@ -43,7 +45,7 @@ const mapStateToProps = (_: any, { rootItemIcon, showSelection }: ProjectsTreePi showSelection: isSelectionVisible(showSelection), }); -const mapDispatchToProps = (dispatch: Dispatch, { loadRootItem, includeCollections, includeFiles, relatedTreePickers, ...props }: ProjectsTreePickerProps): PickedTreePickerProps => ({ +const mapDispatchToProps = (dispatch: Dispatch, { loadRootItem, includeCollections, includeFiles, relatedTreePickers, options, ...props }: ProjectsTreePickerProps): PickedTreePickerProps => ({ onContextMenu: () => { return; }, toggleItemActive: (event, item, pickerId) => { @@ -67,7 +69,7 @@ const mapDispatchToProps = (dispatch: Dispatch, { loadRootItem, includeCollectio : loadProject({ id, pickerId, includeCollections, includeFiles }) ); } else if (!('type' in data) && loadRootItem) { - loadRootItem(item as TreeItem, pickerId, includeCollections, includeFiles); + loadRootItem(item as TreeItem, pickerId, includeCollections, includeFiles, options); } } else if (status === TreeItemStatus.LOADED) { dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id, pickerId })); diff --git a/src/views-components/projects-tree-picker/projects-tree-picker.tsx b/src/views-components/projects-tree-picker/projects-tree-picker.tsx index 10bb5eb8..edb3f261 100644 --- a/src/views-components/projects-tree-picker/projects-tree-picker.tsx +++ b/src/views-components/projects-tree-picker/projects-tree-picker.tsx @@ -33,7 +33,7 @@ export const ProjectsTreePicker = ({ pickerId, ...props }: ProjectsTreePickerPro - + ; }; -- 2.30.2