ObjectAttributes.spec.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. // Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/
  2. vi.setSystemTime('2021-04-09T10:11:12Z')
  3. import { i18n } from '@shared/i18n'
  4. import { getByRole } from '@testing-library/vue'
  5. import { renderComponent } from '@tests/support/components'
  6. import { mockApplicationConfig } from '@tests/support/mock-applicationConfig'
  7. import { mockPermissions } from '@tests/support/mock-permissions'
  8. import { flushPromises } from '@vue/test-utils'
  9. import { keyBy } from 'lodash-es'
  10. import ObjectAttributes from '../ObjectAttributes.vue'
  11. import attributes from './attributes.json'
  12. const attributesByKey = keyBy(attributes, 'name')
  13. describe('common object attributes interface', () => {
  14. test('renders all available attributes', () => {
  15. mockPermissions(['admin.user', 'ticket.agent'])
  16. const object = {
  17. login: 'some_object',
  18. address: 'Berlin, Street, House',
  19. vip: true,
  20. note: 'note',
  21. active: true,
  22. invisible: 'invisible',
  23. objectAttributeValues: [
  24. {
  25. attribute: attributesByKey.date_attribute,
  26. value: '2022-08-19',
  27. __typename: 'ObjectAttributeValue',
  28. },
  29. {
  30. attribute: attributesByKey.textarea_field,
  31. value: 'textarea text',
  32. },
  33. {
  34. attribute: attributesByKey.integer_field,
  35. value: 600,
  36. },
  37. {
  38. attribute: attributesByKey.date_time_field,
  39. value: '2022-08-11T05:00:00.000Z',
  40. },
  41. {
  42. attribute: attributesByKey.single_select,
  43. value: 'key1',
  44. },
  45. {
  46. attribute: attributesByKey.multi_select_field,
  47. value: ['key1', 'key2'],
  48. },
  49. {
  50. attribute: attributesByKey.single_tree_select,
  51. value: 'key1::key1_child1',
  52. },
  53. {
  54. attribute: attributesByKey.multi_tree_select,
  55. value: ['key1', 'key2', 'key2::key2_child1'],
  56. },
  57. {
  58. attribute: attributesByKey.some_url,
  59. value: 'https://url.com',
  60. },
  61. {
  62. attribute: attributesByKey.some_email,
  63. value: 'email@email.com',
  64. },
  65. {
  66. attribute: attributesByKey.phone,
  67. value: '+49 123456789',
  68. },
  69. ],
  70. }
  71. i18n.setTranslationMap(
  72. new Map([
  73. ['FORMAT_DATE', 'dd/mm/yyyy'],
  74. ['FORMAT_DATETIME', 'dd/mm/yyyy HH:MM'],
  75. ]),
  76. )
  77. const view = renderComponent(ObjectAttributes, {
  78. props: {
  79. object,
  80. attributes,
  81. },
  82. router: true,
  83. store: true,
  84. })
  85. const getRegion = (name: string) => view.getByRole('region', { name })
  86. expect(getRegion('Login')).toHaveTextContent(object.login)
  87. expect(getRegion('Address')).toHaveTextContent(object.address)
  88. expect(getRegion('VIP')).toHaveTextContent('yes')
  89. expect(getRegion('Note')).toHaveTextContent(object.note)
  90. expect(getRegion('Active')).toHaveTextContent('yes')
  91. expect(getRegion('Date Attribute')).toHaveTextContent(/19\/08\/2022$/)
  92. expect(getRegion('Textarea Field')).toHaveTextContent('textarea text')
  93. expect(getRegion('Integer Field')).toHaveTextContent('600')
  94. expect(getRegion('DateTime Field')).toHaveTextContent('11/08/2022 05:00')
  95. expect(getRegion('Single Select Field')).toHaveTextContent('Display1')
  96. expect(getRegion('Multi Select Field')).toHaveTextContent(
  97. 'Display1, Display2',
  98. )
  99. expect(getRegion('Single Tree Select Field')).toHaveTextContent(
  100. 'key1::key1_child1',
  101. )
  102. expect(getRegion('Multi Tree Select Field')).toHaveTextContent(
  103. 'key1, key2, key2::key2_child1',
  104. )
  105. expect(
  106. getByRole(getRegion('Phone'), 'link', { name: '+49 123456789' }),
  107. ).toHaveAttribute('href', 'tel:+49123456789')
  108. expect(
  109. getByRole(getRegion('Email'), 'link', { name: 'email@email.com' }),
  110. ).toHaveAttribute('href', 'mailto:email@email.com')
  111. expect(
  112. getByRole(getRegion('Url'), 'link', { name: 'https://url.com' }),
  113. ).toHaveAttribute('href', 'https://url.com')
  114. expect(
  115. view.queryByRole('region', { name: 'Invisible' }),
  116. ).not.toBeInTheDocument()
  117. expect(
  118. view.queryByRole('region', { name: 'Hidden Boolean' }),
  119. ).not.toBeInTheDocument()
  120. })
  121. test('hides attributes without permission', () => {
  122. mockPermissions([])
  123. const object = {
  124. active: true,
  125. }
  126. const view = renderComponent(ObjectAttributes, {
  127. props: {
  128. object,
  129. attributes: [attributesByKey.active],
  130. },
  131. })
  132. expect(view.queryAllByRole('region')).toHaveLength(0)
  133. })
  134. test("don't show empty fields", () => {
  135. const object = {
  136. login: '',
  137. objectAttributesValues: [
  138. {
  139. attribute: attributesByKey.integer_field,
  140. value: 0,
  141. },
  142. {
  143. attribute: attributesByKey.multi_select_field,
  144. value: [],
  145. },
  146. ],
  147. }
  148. const view = renderComponent(ObjectAttributes, {
  149. props: {
  150. object,
  151. attributes: [attributesByKey.login],
  152. },
  153. })
  154. expect(view.queryAllByRole('region')).toHaveLength(0)
  155. })
  156. test('show default, if not defined', () => {
  157. const object = {
  158. login: '',
  159. }
  160. const attribute = {
  161. ...attributesByKey.login,
  162. name: 'login',
  163. display: 'Login',
  164. }
  165. const view = renderComponent(ObjectAttributes, {
  166. props: {
  167. object,
  168. attributes: [
  169. {
  170. ...attribute,
  171. dataOption: { ...attribute.dataOption, default: 'default' },
  172. },
  173. ],
  174. },
  175. })
  176. expect(view.getByRole('region', { name: 'Login' })).toHaveTextContent(
  177. 'default',
  178. )
  179. })
  180. it('translates translatable', () => {
  181. mockPermissions(['admin.user', 'ticket.agent'])
  182. const object = {
  183. vip: true,
  184. single_select: 'key1',
  185. multi_select_field: ['key1', 'key2'],
  186. single_tree_select: 'key1::key1_child1',
  187. multi_tree_select: ['key1', 'key1::key1_child1'],
  188. }
  189. const translatable = (attr: any) => ({
  190. ...attr,
  191. dataOption: {
  192. ...attr.dataOption,
  193. translate: true,
  194. },
  195. })
  196. const attributes = [
  197. translatable(attributesByKey.vip),
  198. translatable(attributesByKey.single_select),
  199. translatable(attributesByKey.multi_select_field),
  200. translatable(attributesByKey.single_tree_select),
  201. translatable(attributesByKey.multi_tree_select),
  202. ]
  203. i18n.setTranslationMap(
  204. new Map([
  205. ['yes', 'sí'],
  206. ['Display1', 'Monitor1'],
  207. ['Display2', 'Monitor2'],
  208. ['key1', 'llave1'],
  209. ['key2', 'llave2'],
  210. ['key1_child1', 'llave1_niño1'],
  211. ]),
  212. )
  213. const view = renderComponent(ObjectAttributes, {
  214. props: {
  215. object,
  216. attributes,
  217. },
  218. router: true,
  219. })
  220. const getRegion = (name: string) => view.getByRole('region', { name })
  221. const vip = getRegion('VIP')
  222. const singleSelect = getRegion('Single Select Field')
  223. const multiSelect = getRegion('Multi Select Field')
  224. const singleTreeSelect = getRegion('Single Tree Select Field')
  225. const multiTreeSelect = getRegion('Multi Tree Select Field')
  226. expect(vip).toHaveTextContent('sí')
  227. expect(singleSelect).toHaveTextContent('Monitor1')
  228. expect(multiSelect).toHaveTextContent('Monitor1, Monitor2')
  229. expect(singleTreeSelect).toHaveTextContent('llave1::llave1_niño1')
  230. expect(multiTreeSelect).toHaveTextContent('llave1, llave1::llave1_niño1')
  231. })
  232. it('renders different dates', async () => {
  233. const object = {
  234. now: '2021-04-09T10:11:12Z',
  235. past: '2021-02-09T10:11:12Z',
  236. future: '2021-10-09T10:11:12Z',
  237. }
  238. const attributes = [
  239. { ...attributesByKey.date_time_field, name: 'now', display: 'now' },
  240. { ...attributesByKey.date_time_field, name: 'past', display: 'past' },
  241. { ...attributesByKey.date_time_field, name: 'future', display: 'future' },
  242. ]
  243. const view = renderComponent(ObjectAttributes, {
  244. props: {
  245. object,
  246. attributes,
  247. },
  248. router: true,
  249. })
  250. const getRegion = (time: string) => view.getByRole('region', { name: time })
  251. expect(getRegion('now')).toHaveTextContent('2021-04-09 10:11')
  252. expect(getRegion('past')).toHaveTextContent('2021-02-09 10:11')
  253. expect(getRegion('future')).toHaveTextContent('2021-10-09 10:11')
  254. mockApplicationConfig({
  255. pretty_date_format: 'relative',
  256. })
  257. await flushPromises()
  258. expect(getRegion('now')).toHaveTextContent('just now')
  259. expect(getRegion('past')).toHaveTextContent('1 month ago')
  260. expect(getRegion('future')).toHaveTextContent('in 6 months')
  261. })
  262. it('doesnt render skipped attributes', () => {
  263. const object = {
  264. skip: 'skip',
  265. show: 'show',
  266. }
  267. const attributes = [
  268. { ...attributesByKey.address, name: 'skip', display: 'skip' },
  269. { ...attributesByKey.address, name: 'show', display: 'show' },
  270. ]
  271. const view = renderComponent(ObjectAttributes, {
  272. props: {
  273. object,
  274. attributes,
  275. skipAttributes: ['skip'],
  276. },
  277. router: true,
  278. })
  279. expect(view.getByRole('region', { name: 'show' })).toBeInTheDocument()
  280. expect(view.queryByRole('region', { name: 'skip' })).not.toBeInTheDocument()
  281. })
  282. it('renders links', () => {
  283. const object = {
  284. objectAttributeValues: [
  285. {
  286. attribute: {
  287. ...attributesByKey.integer_field,
  288. dataOption: {
  289. ...attributesByKey.integer_field.dataOption,
  290. linktemplate: 'https://integer.com/#{render}',
  291. },
  292. },
  293. value: 600,
  294. renderedLink: 'https://integer.com/rendered',
  295. },
  296. {
  297. attribute: attributesByKey.some_url,
  298. value: 'https://url.com',
  299. },
  300. {
  301. attribute: {
  302. ...attributesByKey.some_email,
  303. dataOption: {
  304. ...attributesByKey.integer_field.dataOption,
  305. linktemplate: 'https://email.com/#{render}',
  306. },
  307. },
  308. value: 'email@email.com',
  309. renderedLink: 'https://email.com/rendered',
  310. },
  311. {
  312. attribute: {
  313. ...attributesByKey.phone,
  314. dataOption: {
  315. ...attributesByKey.integer_field.dataOption,
  316. linktemplate: 'https://phone.com/#{render}',
  317. },
  318. },
  319. value: '+49 123456789',
  320. renderedLink: 'https://phone.com/rendered',
  321. },
  322. ],
  323. }
  324. const attributes = object.objectAttributeValues.map((v) => v.attribute)
  325. const view = renderComponent(ObjectAttributes, {
  326. props: {
  327. object,
  328. attributes,
  329. skipAttributes: ['skip'],
  330. },
  331. router: true,
  332. })
  333. const getRegion = (name: string) => view.getByRole('region', { name })
  334. expect(
  335. getByRole(getRegion('Integer Field'), 'link', { name: '600' }),
  336. ).toHaveAttribute('href', 'https://integer.com/rendered')
  337. expect(
  338. getByRole(getRegion('Phone'), 'link', { name: '+49 123456789' }),
  339. ).toHaveAttribute('href', 'https://phone.com/rendered')
  340. expect(
  341. getByRole(getRegion('Email'), 'link', { name: 'email@email.com' }),
  342. ).toHaveAttribute('href', 'https://email.com/rendered')
  343. expect(
  344. getByRole(getRegion('Url'), 'link', { name: 'https://url.com' }),
  345. ).toHaveAttribute('href', 'https://url.com')
  346. })
  347. })