Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[OP#41124] Display loading icon while fetching workpackages #55

Merged
merged 1 commit into from
Feb 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/components/tab/SearchInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
track-by="multiselectKey"
:internal-search="false"
open-direction="below"
:loading="state === 'loading'"
:preselect-first="true"
:preserve-search="true"
@search-change="makeSearchRequest">
<template #option="{option}">
<div class="searchList">
Expand Down Expand Up @@ -87,8 +90,6 @@ export default {
return t('integration_openproject', 'No OpenProject account connected')
} else if (this.state === 'error') {
return t('integration_openproject', 'Error connecting to OpenProject')
} else if (this.state === 'loading') {
return t('integration_openproject', 'Wait while we fetch work packages')
}
return ''
},
Expand All @@ -113,11 +114,11 @@ export default {
}
const url = generateUrl('/apps/integration_openproject/work-packages')
if (this.search.length > 3) {
this.state = 'loading'
const req = {}
req.params = {
searchQuery: this.search,
}
this.state = 'loading'
const response = await axios.get(url, req)
if (response.status === 200) {
this.state = 'ok'
Expand Down
56 changes: 53 additions & 3 deletions tests/jest/components/tab/SearchInput.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import { createLocalVue, mount, shallowMount } from '@vue/test-utils'
import SearchInput from '../../../../src/components/tab/SearchInput'
import workPackagesSearchResponse from '../../fixtures/workPackagesSearchResponse.json'
import workPackagesSearchResponseNoAssignee from '../../fixtures/workPackagesSearchResponseNoAssignee.json'
import searchReqResponse from '../../fixtures/workPackageSearchReqResponse.json'
import axios from '@nextcloud/axios'

jest.mock('@nextcloud/axios')
jest.mock('@nextcloud/l10n', () => ({
translate: jest.fn((app, msg) => msg),
getLanguage: jest.fn(() => ''),
Expand Down Expand Up @@ -34,9 +37,6 @@ describe('SearchInput.vue tests', () => {
}, {
state: 'error',
message: 'Error connecting to OpenProject',
}, {
state: 'loading',
message: 'Wait while we fetch work packages',
}])('should be displayed depending upon the state', async (cases) => {
const stateSelector = '.stateMsg'
const wrapper = shallowMount(SearchInput, {
Expand All @@ -62,6 +62,7 @@ describe('SearchInput.vue tests', () => {
const statusSelector = '.filterProjectTypeStatus__status'
const typeSelector = '.filterProjectTypeStatus__type'
const assigneeSelector = '.filterAssignee'
const loadingIconSelector = '.icon-loading-small'
it('should not be displayed if the length of words in searchbar is less than or equal to three', async () => {
const wrapper = mountSearchInput()
const textInput = wrapper.find(inputSelector)
Expand Down Expand Up @@ -145,5 +146,54 @@ describe('SearchInput.vue tests', () => {
const assignee = wrapper.find(assigneeSelector)
expect(assignee.exists()).toBeFalsy()
})

it('should display a loading button when the work package is being fetched', async () => {
const wrapper = mountSearchInput()
let loadingIcon = wrapper.find(loadingIconSelector)
expect(loadingIcon.exists()).toBeFalsy()
await wrapper.setData({
state: 'loading',
})
loadingIcon = wrapper.find(loadingIconSelector)
expect(loadingIcon.exists()).toBeTruthy()
})
})

describe('getWorkPackageColorAttributes', () => {
it.each([
{
status: 200,
state: 'ok',
},
{
status: 401,
state: 'no-token',
},
{
status: 400,
state: 'error',
},
{
status: 500,
state: 'error',
},
{
status: 404,
state: 'error',
},
])('sets states according to status', async (cases) => {
axios.get.mockImplementation(() =>
Promise.resolve({
data: {
color: 'red',
},
Comment on lines +187 to +189
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

data is also redundant here imo

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it gives me the error TypeError: Cannot read property 'color' of undefined, if I don't define data

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be done in an other PR, but I would suggest to improve getWorkPackageColorAttributes
it checks if (response.status === 200) { and the assumes that the response has a color attribute, but what if not?
Should be something like if (response.status === 200 && response.data.color !== undefined) {

status: cases.status,
},
),
)
const wrapper = mountSearchInput()
await wrapper.vm.processWorkPackages(searchReqResponse)
expect(wrapper.vm.state).toBe(cases.state)
})
})
})
203 changes: 203 additions & 0 deletions tests/jest/fixtures/workPackageSearchReqResponse.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
[{
"derivedStartDate": "2022-01-15",
"derivedDueDate": "2022-03-08",
"_type": "WorkPackage",
"id": 2,
"lockVersion": 0,
"subject": "Organize open source conference",
"description": {
"format": "markdown",
"raw": null,
"html": ""
},
"scheduleManually": false,
"startDate": "2022-01-15",
"dueDate": "2022-03-26",
"estimatedTime": null,
"derivedEstimatedTime": null,
"percentageDone": 0,
"createdAt": "2022-01-12T08:53:29Z",
"updatedAt": "2022-01-12T08:53:29Z",
"_links": {
"attachments": {
"href": "/api/v3/work_packages/2/attachments"
},
"addAttachment": {
"href": "/api/v3/work_packages/2/attachments",
"method": "post"
},
"self": {
"href": "/api/v3/work_packages/2",
"title": "Organize open source conference"
},
"update": {
"href": "/api/v3/work_packages/2/form",
"method": "post"
},
"schema": {
"href": "/api/v3/work_packages/schemas/1-3"
},
"updateImmediately": {
"href": "/api/v3/work_packages/2",
"method": "patch"
},
"delete": {
"href": "/api/v3/work_packages/2",
"method": "delete"
},
"move": {
"href": "/work_packages/2/move/new",
"type": "text/html",
"title": "Move Organize open source conference"
},
"copy": {
"href": "/work_packages/2/copy",
"title": "Copy Organize open source conference"
},
"pdf": {
"href": "/work_packages/2.pdf",
"type": "application/pdf",
"title": "Export as PDF"
},
"atom": {
"href": "/work_packages/2.atom",
"type": "application/rss+xml",
"title": "Atom feed"
},
"availableRelationCandidates": {
"href": "/api/v3/work_packages/2/available_relation_candidates",
"title": "Potential work packages to relate to"
},
"customFields": {
"href": "/projects/demo-project/settings/custom_fields",
"type": "text/html",
"title": "Custom fields"
},
"configureForm": {
"href": "/types/3/edit?tab=form_configuration",
"type": "text/html",
"title": "Configure form"
},
"activities": {
"href": "/api/v3/work_packages/2/activities"
},
"availableWatchers": {
"href": "/api/v3/work_packages/2/available_watchers"
},
"relations": {
"href": "/api/v3/work_packages/2/relations"
},
"revisions": {
"href": "/api/v3/work_packages/2/revisions"
},
"watchers": {
"href": "/api/v3/work_packages/2/watchers"
},
"addWatcher": {
"href": "/api/v3/work_packages/2/watchers",
"method": "post",
"payload": {
"user": {
"href": "/api/v3/users/{user_id}"
}
},
"templated": true
},
"removeWatcher": {
"href": "/api/v3/work_packages/2/watchers/{user_id}",
"method": "delete",
"templated": true
},
"addRelation": {
"href": "/api/v3/work_packages/2/relations",
"method": "post",
"title": "Add relation"
},
"addChild": {
"href": "/api/v3/projects/demo-project/work_packages",
"method": "post",
"title": "Add child of Organize open source conference"
},
"changeParent": {
"href": "/api/v3/work_packages/2",
"method": "patch",
"title": "Change parent of Organize open source conference"
},
"addComment": {
"href": "/api/v3/work_packages/2/activities",
"method": "post",
"title": "Add comment"
},
"previewMarkup": {
"href": "/api/v3/render/markdown?context=/api/v3/work_packages/2",
"method": "post"
},
"category": {
"href": null
},
"type": {
"href": "/api/v3/types/3",
"title": "Phase"
},
"priority": {
"href": "/api/v3/priorities/8",
"title": "Normal"
},
"project": {
"href": "/api/v3/projects/1",
"title": "Demo project"
},
"status": {
"href": "/api/v3/statuses/7",
"title": "In progress"
},
"author": {
"href": "/api/v3/users/1"
},
"responsible": {
"href": null
},
"assignee": {
"href": "/api/v3/users/1",
"title": "System"
},
"version": {
"href": null
},
"github_pull_requests": {
"href": "/api/v3/work_packages/2/github_pull_requests",
"title": "GitHub pull requests"
},
"watch": {
"href": "/api/v3/work_packages/2/watchers",
"method": "post",
"payload": {
"user": {
"href": "/api/v3/users/3"
}
}
},
"children": [
{
"href": "/api/v3/work_packages/3",
"title": "Set date and location of conference"
},
{
"href": "/api/v3/work_packages/4",
"title": "Setup conference website"
},
{
"href": "/api/v3/work_packages/5",
"title": "Invite attendees to conference"
}
],
"ancestors": [],
"parent": {
"href": null,
"title": null
},
"customActions": []
}
}

]