guided-setup-manual-channel-email.spec.ts 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001
  1. // Copyright (C) 2012-2025 Zammad Foundation, https://zammad-foundation.org/
  2. import { getNode } from '@formkit/core'
  3. import {
  4. getByLabelText,
  5. getByRole,
  6. getByText,
  7. queryByLabelText,
  8. queryByText,
  9. } from '@testing-library/vue'
  10. import { visitView } from '#tests/support/components/visitView.ts'
  11. import { mockApplicationConfig } from '#tests/support/mock-applicationConfig.ts'
  12. import { mockAuthentication } from '#tests/support/mock-authentication.ts'
  13. import { mockPermissions } from '#tests/support/mock-permissions.ts'
  14. import { mockFormUpdaterQuery } from '#shared/components/Form/graphql/queries/formUpdater.mocks.ts'
  15. import {
  16. EnumChannelEmailInboundAdapter,
  17. EnumChannelEmailOutboundAdapter,
  18. EnumChannelEmailSsl,
  19. EnumFormUpdaterId,
  20. EnumSystemSetupInfoStatus,
  21. } from '#shared/graphql/types.ts'
  22. import {
  23. mockChannelEmailAddMutation,
  24. waitForChannelEmailAddMutationCalls,
  25. } from '#desktop/entities/channel-email/graphql/mutations/channelEmailAdd.mocks.ts'
  26. import { mockChannelEmailGuessConfigurationMutation } from '#desktop/entities/channel-email/graphql/mutations/channelEmailGuessConfiguration.mocks.ts'
  27. import { mockChannelEmailValidateConfigurationInboundMutation } from '#desktop/entities/channel-email/graphql/mutations/channelEmailValidateConfigurationInbound.mocks.ts'
  28. import { mockChannelEmailValidateConfigurationOutboundMutation } from '#desktop/entities/channel-email/graphql/mutations/channelEmailValidateConfigurationOutbound.mocks.ts'
  29. import { mockChannelEmailValidateConfigurationRoundtripMutation } from '#desktop/entities/channel-email/graphql/mutations/channelEmailValidateConfigurationRoundtrip.mocks.ts'
  30. import { mockSystemSetupInfoQuery } from '../graphql/queries/systemSetupInfo.mocks.ts'
  31. const inboundConfiguration = {
  32. adapter: EnumChannelEmailInboundAdapter.Imap,
  33. host: 'mail.test.dc.zammad.com',
  34. port: 993,
  35. ssl: EnumChannelEmailSsl.Ssl,
  36. user: 'zammad@mail.test.dc.zammad.com',
  37. password: 'zammad',
  38. sslVerify: true,
  39. folder: 'INBOX',
  40. }
  41. const outboundConfiguration = {
  42. adapter: EnumChannelEmailOutboundAdapter.Smtp,
  43. host: 'mail.test.dc.zammad.com',
  44. port: 25,
  45. user: 'zammad@mail.test.dc.zammad.com',
  46. password: 'zammad',
  47. sslVerify: false,
  48. }
  49. const sslVerificationWarningText =
  50. 'Turning off SSL verification is a security risk and should be used only temporary. Use this option at your own risk!'
  51. describe('guided setup manual channel email', () => {
  52. describe('when system is not ready', () => {
  53. beforeEach(() => {
  54. mockApplicationConfig({
  55. system_init_done: false,
  56. })
  57. })
  58. it('redirects to guided setup start', async () => {
  59. mockSystemSetupInfoQuery({
  60. systemSetupInfo: {
  61. status: EnumSystemSetupInfoStatus.New,
  62. type: null,
  63. },
  64. })
  65. const view = await visitView('/guided-setup/manual/channels/email')
  66. await vi.waitFor(() => {
  67. expect(
  68. view,
  69. 'correctly redirects to guided setup start screen',
  70. ).toHaveCurrentUrl('/guided-setup')
  71. })
  72. view.getByText('Set up a new system')
  73. })
  74. })
  75. describe('when system is ready for optional steps', () => {
  76. beforeEach(() => {
  77. mockApplicationConfig({
  78. system_init_done: true,
  79. })
  80. mockPermissions(['admin'])
  81. mockAuthentication(true)
  82. mockFormUpdaterQuery((variables) => {
  83. switch (variables.formUpdaterId) {
  84. case EnumFormUpdaterId.FormUpdaterUpdaterGuidedSetupEmailArchive:
  85. return {
  86. formUpdater: {
  87. fields: {
  88. archive_state_id: {
  89. initialValue: 4,
  90. options: [
  91. {
  92. value: 1,
  93. label: 'new',
  94. },
  95. {
  96. value: 2,
  97. label: 'open',
  98. },
  99. {
  100. value: 3,
  101. label: 'pending reminder',
  102. },
  103. {
  104. value: 4,
  105. label: 'closed',
  106. },
  107. {
  108. value: 5,
  109. label: 'merged',
  110. },
  111. {
  112. value: 6,
  113. label: 'pending close',
  114. },
  115. ],
  116. },
  117. },
  118. },
  119. }
  120. case EnumFormUpdaterId.FormUpdaterUpdaterGuidedSetupEmailOutbound:
  121. return {
  122. formUpdater: {
  123. fields: {
  124. adapter: {
  125. initialValue: 'smtp',
  126. options: [
  127. {
  128. value: 'smtp',
  129. label:
  130. 'SMTP - configure your own outgoing SMTP settings',
  131. },
  132. {
  133. value: 'sendmail',
  134. label:
  135. 'Local MTA (Sendmail/Postfix/Exim/â\u0080¦) - use server setup',
  136. },
  137. ],
  138. },
  139. },
  140. },
  141. }
  142. case EnumFormUpdaterId.FormUpdaterUpdaterGuidedSetupEmailInbound:
  143. default:
  144. return {
  145. formUpdater: {
  146. fields: {
  147. adapter: {
  148. initialValue: 'imap',
  149. options: [
  150. {
  151. value: 'imap',
  152. label: 'IMAP',
  153. },
  154. {
  155. value: 'pop3',
  156. label: 'POP3',
  157. },
  158. ],
  159. },
  160. },
  161. },
  162. }
  163. }
  164. })
  165. })
  166. it('can redirect to invite step when guess is successful', async () => {
  167. const view = await visitView('/guided-setup/manual/channels/email')
  168. expect(view.getByText('Email Account')).toBeInTheDocument()
  169. expect(view.getByRole('button', { name: 'Go Back' })).toBeInTheDocument()
  170. const accountForm = view.getByTestId('channel-email-account')
  171. await view.events.type(
  172. getByLabelText(accountForm, 'Full name'),
  173. 'Zammad Helpdesk',
  174. )
  175. await view.events.type(
  176. getByLabelText(accountForm, 'Email address'),
  177. 'zammad@mail.test.dc.zammad.com',
  178. )
  179. await view.events.type(getByLabelText(accountForm, 'Password'), 'zammad')
  180. mockChannelEmailGuessConfigurationMutation({
  181. channelEmailGuessConfiguration: {
  182. result: {
  183. inboundConfiguration,
  184. outboundConfiguration,
  185. mailboxStats: {
  186. contentMessages: 0,
  187. },
  188. },
  189. },
  190. })
  191. mockChannelEmailValidateConfigurationRoundtripMutation({
  192. channelEmailValidateConfigurationRoundtrip: {
  193. success: true,
  194. },
  195. })
  196. await view.events.click(
  197. view.getByRole('button', {
  198. name: 'Connect and Continue',
  199. }),
  200. )
  201. await vi.waitFor(() => {
  202. expect(
  203. view,
  204. 'correctly redirects to guided setup invite step',
  205. ).toHaveCurrentUrl('/guided-setup/manual/invite')
  206. })
  207. })
  208. it('can show inbound configuration form when guess is unsuccessful', async () => {
  209. const view = await visitView('/guided-setup/manual/channels/email')
  210. const accountForm = view.getByTestId('channel-email-account')
  211. await view.events.type(
  212. getByLabelText(accountForm, 'Full name'),
  213. 'Zammad Helpdesk',
  214. )
  215. await view.events.type(
  216. getByLabelText(accountForm, 'Email address'),
  217. 'zammad@mail.test.dc.zammad.com',
  218. )
  219. await view.events.type(getByLabelText(accountForm, 'Password'), 'zammad')
  220. mockChannelEmailGuessConfigurationMutation({
  221. channelEmailGuessConfiguration: {
  222. result: {
  223. inboundConfiguration: null,
  224. outboundConfiguration: null,
  225. },
  226. },
  227. })
  228. await view.events.click(
  229. view.getByRole('button', {
  230. name: 'Connect and Continue',
  231. }),
  232. )
  233. expect(accountForm).not.toBeVisible()
  234. const inboundForm = view.getByTestId('channel-email-inbound')
  235. expect(inboundForm).toBeVisible()
  236. expect(getByRole(inboundForm, 'alert')).toHaveTextContent(
  237. 'The server settings could not be automatically detected. Please configure them manually.',
  238. )
  239. expect(view.getByRole('button', { name: 'Continue' })).toBeInTheDocument()
  240. })
  241. it('can show inbound configuration form when roundtrip is unsuccessful', async () => {
  242. const view = await visitView('/guided-setup/manual/channels/email')
  243. const accountForm = view.getByTestId('channel-email-account')
  244. await view.events.type(
  245. getByLabelText(accountForm, 'Full name'),
  246. 'Zammad Helpdesk',
  247. )
  248. await view.events.type(
  249. getByLabelText(accountForm, 'Email address'),
  250. 'zammad@mail.test.dc.zammad.com',
  251. )
  252. await view.events.type(getByLabelText(accountForm, 'Password'), 'zammad')
  253. mockChannelEmailGuessConfigurationMutation({
  254. channelEmailGuessConfiguration: {
  255. result: {
  256. inboundConfiguration,
  257. outboundConfiguration,
  258. mailboxStats: {
  259. contentMessages: 0,
  260. },
  261. },
  262. },
  263. })
  264. mockChannelEmailValidateConfigurationRoundtripMutation({
  265. channelEmailValidateConfigurationRoundtrip: {
  266. success: false,
  267. errors: [
  268. {
  269. message: 'Something went wrong',
  270. field: 'inbound.adapter',
  271. },
  272. ],
  273. },
  274. })
  275. await view.events.click(
  276. view.getByRole('button', {
  277. name: 'Connect and Continue',
  278. }),
  279. )
  280. expect(accountForm).not.toBeVisible()
  281. const inboundForm = view.getByTestId('channel-email-inbound')
  282. expect(inboundForm).toBeVisible()
  283. expect(getByText(inboundForm, 'Something went wrong')).toBeInTheDocument()
  284. })
  285. it('can show a form error when adding email channel is unsuccessful', async () => {
  286. const view = await visitView('/guided-setup/manual/channels/email')
  287. const accountForm = view.getByTestId('channel-email-account')
  288. await view.events.type(
  289. getByLabelText(accountForm, 'Full name'),
  290. 'Zammad Helpdesk',
  291. )
  292. await view.events.type(
  293. getByLabelText(accountForm, 'Email address'),
  294. 'zammad@mail.test.dc.zammad.com',
  295. )
  296. await view.events.type(getByLabelText(accountForm, 'Password'), 'zammad')
  297. mockChannelEmailGuessConfigurationMutation({
  298. channelEmailGuessConfiguration: {
  299. result: {
  300. inboundConfiguration,
  301. outboundConfiguration,
  302. mailboxStats: {
  303. contentMessages: 0,
  304. },
  305. },
  306. },
  307. })
  308. mockChannelEmailValidateConfigurationRoundtripMutation({
  309. channelEmailValidateConfigurationRoundtrip: {
  310. success: true,
  311. },
  312. })
  313. mockChannelEmailAddMutation({
  314. channelEmailAdd: {
  315. errors: [
  316. {
  317. message: 'The provided password is invalid.',
  318. field: 'password',
  319. },
  320. ],
  321. },
  322. })
  323. await view.events.click(
  324. view.getByRole('button', {
  325. name: 'Connect and Continue',
  326. }),
  327. )
  328. expect(accountForm).toBeVisible()
  329. expect(
  330. getByText(accountForm, 'The provided password is invalid.'),
  331. ).toBeInTheDocument()
  332. })
  333. it('can show inbound messages form when some messages are detected', async () => {
  334. const view = await visitView('/guided-setup/manual/channels/email')
  335. const accountForm = view.getByTestId('channel-email-account')
  336. await view.events.type(
  337. getByLabelText(accountForm, 'Full name'),
  338. 'Zammad Helpdesk',
  339. )
  340. await view.events.type(
  341. getByLabelText(accountForm, 'Email address'),
  342. 'zammad@mail.test.dc.zammad.com',
  343. )
  344. await view.events.type(getByLabelText(accountForm, 'Password'), 'zammad')
  345. mockChannelEmailGuessConfigurationMutation({
  346. channelEmailGuessConfiguration: {
  347. result: {
  348. inboundConfiguration: null,
  349. outboundConfiguration: null,
  350. },
  351. },
  352. })
  353. await view.events.click(
  354. view.getByRole('button', {
  355. name: 'Connect and Continue',
  356. }),
  357. )
  358. const inboundForm = view.getByTestId('channel-email-inbound')
  359. await view.events.type(
  360. getByLabelText(inboundForm, 'Host'),
  361. 'mail.test.dc.zammad.com',
  362. )
  363. await getNode('channel-email-inbound')?.settled
  364. mockChannelEmailValidateConfigurationInboundMutation({
  365. channelEmailValidateConfigurationInbound: {
  366. success: true,
  367. mailboxStats: {
  368. contentMessages: 3,
  369. },
  370. },
  371. })
  372. await view.events.click(
  373. view.getByRole('button', {
  374. name: 'Continue',
  375. }),
  376. )
  377. expect(inboundForm).not.toBeVisible()
  378. const inboundMessagesForm = view.getByTestId(
  379. 'channel-email-inbound-messages',
  380. )
  381. expect(inboundMessagesForm).toBeVisible()
  382. expect(inboundMessagesForm).toHaveTextContent(
  383. '3 email(s) were found in your mailbox. They will all be moved from your mailbox into Zammad.',
  384. )
  385. expect(inboundMessagesForm).toHaveTextContent(
  386. 'You can import some of your emails as an "archive", which means that no notifications are sent and the tickets will be in a target state that you define.',
  387. )
  388. expect(inboundMessagesForm).toHaveTextContent(
  389. 'You can find archived emails in Zammad anytime using the search function, like for any other ticket.',
  390. )
  391. expect(
  392. getByLabelText(inboundMessagesForm, 'Archive emails'),
  393. ).toBeInTheDocument()
  394. expect(
  395. getByLabelText(inboundMessagesForm, 'Archive cut-off time'),
  396. ).toBeDescribedBy(
  397. 'Emails before the cut-off time are imported as archived tickets. Emails after the cut-off time are imported as regular tickets.',
  398. )
  399. expect(
  400. getByLabelText(inboundMessagesForm, 'Archive ticket target state'),
  401. ).toBeInTheDocument()
  402. await view.events.click(
  403. getByLabelText(inboundMessagesForm, 'Archive emails'),
  404. )
  405. expect(
  406. queryByLabelText(inboundMessagesForm, 'Archive cut-off time'),
  407. ).not.toBeInTheDocument()
  408. expect(
  409. queryByLabelText(inboundMessagesForm, 'Archive ticket target state'),
  410. ).not.toBeInTheDocument()
  411. expect(view.getByRole('button', { name: 'Continue' })).toBeInTheDocument()
  412. })
  413. it('can show outbound configuration form when guess is unsuccessful', async () => {
  414. const view = await visitView('/guided-setup/manual/channels/email')
  415. const accountForm = view.getByTestId('channel-email-account')
  416. await view.events.type(
  417. getByLabelText(accountForm, 'Full name'),
  418. 'Zammad Helpdesk',
  419. )
  420. await view.events.type(
  421. getByLabelText(accountForm, 'Email address'),
  422. 'zammad@mail.test.dc.zammad.com',
  423. )
  424. await view.events.type(getByLabelText(accountForm, 'Password'), 'zammad')
  425. mockChannelEmailGuessConfigurationMutation({
  426. channelEmailGuessConfiguration: {
  427. result: {
  428. inboundConfiguration: null,
  429. outboundConfiguration: null,
  430. },
  431. },
  432. })
  433. await view.events.click(
  434. view.getByRole('button', {
  435. name: 'Connect and Continue',
  436. }),
  437. )
  438. const inboundForm = view.getByTestId('channel-email-inbound')
  439. await view.events.type(
  440. getByLabelText(inboundForm, 'Host'),
  441. 'mail.test.dc.zammad.com',
  442. )
  443. await getNode('channel-email-inbound')?.settled
  444. mockChannelEmailValidateConfigurationInboundMutation({
  445. channelEmailValidateConfigurationInbound: {
  446. success: true,
  447. mailboxStats: {
  448. contentMessages: 0,
  449. },
  450. },
  451. })
  452. await view.events.click(
  453. view.getByRole('button', {
  454. name: 'Continue',
  455. }),
  456. )
  457. expect(inboundForm).not.toBeVisible()
  458. const outboundForm = view.getByTestId('channel-email-outbound')
  459. expect(outboundForm).toBeVisible()
  460. expect(
  461. view.getByRole('button', { name: 'Save and Continue' }),
  462. ).toBeInTheDocument()
  463. })
  464. it('can show outbound configuration form when roundtrip is unsuccessful', async () => {
  465. const view = await visitView('/guided-setup/manual/channels/email')
  466. const accountForm = view.getByTestId('channel-email-account')
  467. await view.events.type(
  468. getByLabelText(accountForm, 'Full name'),
  469. 'Zammad Helpdesk',
  470. )
  471. await view.events.type(
  472. getByLabelText(accountForm, 'Email address'),
  473. 'zammad@mail.test.dc.zammad.com',
  474. )
  475. await view.events.type(getByLabelText(accountForm, 'Password'), 'zammad')
  476. mockChannelEmailGuessConfigurationMutation({
  477. channelEmailGuessConfiguration: {
  478. result: {
  479. inboundConfiguration,
  480. outboundConfiguration,
  481. mailboxStats: {
  482. contentMessages: 0,
  483. },
  484. },
  485. },
  486. })
  487. mockChannelEmailValidateConfigurationRoundtripMutation({
  488. channelEmailValidateConfigurationRoundtrip: {
  489. success: false,
  490. errors: [
  491. {
  492. message: 'Something went wrong',
  493. field: 'outbound.adapter',
  494. },
  495. ],
  496. },
  497. })
  498. await view.events.click(
  499. view.getByRole('button', {
  500. name: 'Connect and Continue',
  501. }),
  502. )
  503. expect(accountForm).not.toBeVisible()
  504. const outboundForm = view.getByTestId('channel-email-outbound')
  505. expect(outboundForm).toBeVisible()
  506. expect(
  507. getByText(outboundForm, 'Something went wrong'),
  508. ).toBeInTheDocument()
  509. })
  510. it('can add email channel and redirect to invite step', async () => {
  511. const view = await visitView('/guided-setup/manual/channels/email')
  512. const accountForm = view.getByTestId('channel-email-account')
  513. await view.events.type(
  514. getByLabelText(accountForm, 'Full name'),
  515. 'Zammad Helpdesk',
  516. )
  517. await view.events.type(
  518. getByLabelText(accountForm, 'Email address'),
  519. 'zammad@mail.test.dc.zammad.com',
  520. )
  521. await view.events.type(getByLabelText(accountForm, 'Password'), 'zammad')
  522. mockChannelEmailGuessConfigurationMutation({
  523. channelEmailGuessConfiguration: {
  524. result: {
  525. inboundConfiguration: null,
  526. outboundConfiguration: null,
  527. },
  528. },
  529. })
  530. await view.events.click(
  531. view.getByRole('button', {
  532. name: 'Connect and Continue',
  533. }),
  534. )
  535. const inboundForm = view.getByTestId('channel-email-inbound')
  536. await view.events.type(
  537. getByLabelText(inboundForm, 'Host'),
  538. 'mail.test.dc.zammad.com',
  539. )
  540. await getNode('channel-email-inbound')?.settled
  541. mockChannelEmailValidateConfigurationInboundMutation({
  542. channelEmailValidateConfigurationInbound: {
  543. success: true,
  544. mailboxStats: {
  545. contentMessages: 1,
  546. },
  547. },
  548. })
  549. await view.events.click(
  550. view.getByRole('button', {
  551. name: 'Continue',
  552. }),
  553. )
  554. const inboundMessagesForm = view.getByTestId(
  555. 'channel-email-inbound-messages',
  556. )
  557. await view.events.type(
  558. getByLabelText(inboundMessagesForm, 'Archive cut-off time'),
  559. '2025-01-01 00:00',
  560. )
  561. await view.events.keyboard('{enter}')
  562. await getNode('channel-email-inbound-messages')?.settled
  563. await view.events.click(
  564. view.getByRole('button', {
  565. name: 'Continue',
  566. }),
  567. )
  568. const outboundForm = view.getByTestId('channel-email-outbound')
  569. await view.events.type(getByLabelText(outboundForm, 'Port'), '25')
  570. mockChannelEmailValidateConfigurationOutboundMutation({
  571. channelEmailValidateConfigurationOutbound: {
  572. success: true,
  573. },
  574. })
  575. mockChannelEmailValidateConfigurationRoundtripMutation({
  576. channelEmailValidateConfigurationRoundtrip: {
  577. success: true,
  578. },
  579. })
  580. mockChannelEmailAddMutation({
  581. channelEmailAdd: {
  582. channel: {
  583. options: {},
  584. group: {
  585. id: 'gid://zammad/Group/1',
  586. },
  587. },
  588. },
  589. })
  590. await view.events.click(
  591. view.getByRole('button', {
  592. name: 'Save and Continue',
  593. }),
  594. )
  595. const calls = await waitForChannelEmailAddMutationCalls()
  596. expect(calls.at(-1)?.variables).toEqual({
  597. input: {
  598. emailAddress: 'zammad@mail.test.dc.zammad.com',
  599. emailRealname: 'Zammad Helpdesk',
  600. inboundConfiguration: {
  601. adapter: 'imap',
  602. folder: '',
  603. host: 'mail.test.dc.zammad.com',
  604. keepOnServer: false,
  605. password: 'zammad',
  606. port: 993,
  607. ssl: 'ssl',
  608. sslVerify: true,
  609. user: 'zammad@mail.test.dc.zammad.com',
  610. archive: true,
  611. archiveBefore: '2025-01-01T00:00:00.000Z',
  612. archiveStateId: 4,
  613. },
  614. outboundConfiguration: {
  615. adapter: 'smtp',
  616. host: 'mail.test.dc.zammad.com',
  617. password: 'zammad',
  618. port: 25,
  619. sslVerify: false,
  620. user: 'zammad@mail.test.dc.zammad.com',
  621. },
  622. },
  623. })
  624. await vi.waitFor(() => {
  625. expect(view).toHaveCurrentUrl('/guided-setup/manual/invite')
  626. })
  627. })
  628. it('can show warning when SSL/STARTTLS is used and SSL verification is turned off in inbound form', async () => {
  629. const view = await visitView('/guided-setup/manual/channels/email')
  630. const accountForm = view.getByTestId('channel-email-account')
  631. await view.events.type(
  632. getByLabelText(accountForm, 'Full name'),
  633. 'Zammad Helpdesk',
  634. )
  635. await view.events.type(
  636. getByLabelText(accountForm, 'Email address'),
  637. 'zammad@mail.test.dc.zammad.com',
  638. )
  639. await view.events.type(getByLabelText(accountForm, 'Password'), 'zammad')
  640. mockChannelEmailGuessConfigurationMutation({
  641. channelEmailGuessConfiguration: {
  642. result: {
  643. inboundConfiguration: null,
  644. outboundConfiguration: null,
  645. },
  646. },
  647. })
  648. await view.events.click(
  649. view.getByRole('button', {
  650. name: 'Connect and Continue',
  651. }),
  652. )
  653. const inboundForm = view.getByTestId('channel-email-inbound')
  654. let alert = queryByText(inboundForm, sslVerificationWarningText)
  655. expect(alert).not.toBeInTheDocument()
  656. await view.events.click(getByLabelText(inboundForm, 'SSL verification'))
  657. alert = getByText(inboundForm, sslVerificationWarningText)
  658. expect(alert?.role).toBe('alert')
  659. await view.events.click(getByLabelText(inboundForm, 'SSL verification'))
  660. expect(alert).not.toBeInTheDocument()
  661. await view.events.click(getByLabelText(inboundForm, 'SSL verification'))
  662. alert = getByText(inboundForm, sslVerificationWarningText)
  663. expect(alert).toBeInTheDocument()
  664. const sslField = view.getByLabelText('SSL/STARTTLS')
  665. await view.events.click(sslField)
  666. await view.events.click(view.getAllByRole('option')[0])
  667. expect(alert).not.toBeInTheDocument()
  668. await view.events.click(sslField)
  669. await view.events.click(view.getAllByRole('option')[2])
  670. alert = queryByText(inboundForm, sslVerificationWarningText)
  671. expect(alert).not.toBeInTheDocument()
  672. await view.events.click(getByLabelText(inboundForm, 'SSL verification'))
  673. alert = getByText(inboundForm, sslVerificationWarningText)
  674. expect(alert).toBeInTheDocument()
  675. })
  676. it('can show warning when secure port is used and SSL verification is turned off in outbound form', async () => {
  677. const view = await visitView('/guided-setup/manual/channels/email')
  678. const accountForm = view.getByTestId('channel-email-account')
  679. await view.events.type(
  680. getByLabelText(accountForm, 'Full name'),
  681. 'Zammad Helpdesk',
  682. )
  683. await view.events.type(
  684. getByLabelText(accountForm, 'Email address'),
  685. 'zammad@mail.test.dc.zammad.com',
  686. )
  687. await view.events.type(getByLabelText(accountForm, 'Password'), 'zammad')
  688. mockChannelEmailGuessConfigurationMutation({
  689. channelEmailGuessConfiguration: {
  690. result: {
  691. inboundConfiguration: null,
  692. outboundConfiguration: null,
  693. },
  694. },
  695. })
  696. await view.events.click(
  697. view.getByRole('button', {
  698. name: 'Connect and Continue',
  699. }),
  700. )
  701. const inboundForm = view.getByTestId('channel-email-inbound')
  702. await view.events.type(
  703. getByLabelText(inboundForm, 'Host'),
  704. 'mail.test.dc.zammad.com',
  705. )
  706. await getNode('channel-email-inbound')?.settled
  707. mockChannelEmailValidateConfigurationInboundMutation({
  708. channelEmailValidateConfigurationInbound: {
  709. success: true,
  710. mailboxStats: {
  711. contentMessages: 0,
  712. },
  713. },
  714. })
  715. await view.events.click(
  716. view.getByRole('button', {
  717. name: 'Continue',
  718. }),
  719. )
  720. const outboundForm = view.getByTestId('channel-email-outbound')
  721. let alert = queryByText(outboundForm, sslVerificationWarningText)
  722. expect(alert).not.toBeInTheDocument()
  723. expect(
  724. getByLabelText(outboundForm, 'SSL verification'),
  725. ).not.toBeDisabled()
  726. await view.events.click(getByLabelText(outboundForm, 'SSL verification'))
  727. alert = getByText(outboundForm, sslVerificationWarningText)
  728. expect(alert?.role).toBe('alert')
  729. await view.events.click(getByLabelText(outboundForm, 'SSL verification'))
  730. expect(alert).not.toBeInTheDocument()
  731. await view.events.click(getByLabelText(outboundForm, 'SSL verification'))
  732. alert = getByText(outboundForm, sslVerificationWarningText)
  733. expect(alert).toBeInTheDocument()
  734. await view.events.type(getByLabelText(outboundForm, 'Port'), '25')
  735. await vi.waitFor(() => {
  736. expect(alert).not.toBeInTheDocument()
  737. expect(getByLabelText(outboundForm, 'SSL verification')).toBeDisabled()
  738. })
  739. await view.events.clear(getByLabelText(outboundForm, 'Port'))
  740. await vi.waitFor(() => {
  741. expect(
  742. getByLabelText(outboundForm, 'SSL verification'),
  743. ).not.toBeDisabled()
  744. })
  745. await view.events.click(getByLabelText(outboundForm, 'SSL verification'))
  746. await vi.waitFor(() => {
  747. alert = getByText(outboundForm, sslVerificationWarningText)
  748. expect(alert).toBeInTheDocument()
  749. expect(
  750. getByLabelText(outboundForm, 'SSL verification'),
  751. ).not.toBeDisabled()
  752. })
  753. await view.events.type(getByLabelText(outboundForm, 'Port'), '465')
  754. await vi.waitFor(() => {
  755. expect(
  756. getByLabelText(outboundForm, 'SSL verification'),
  757. ).not.toBeDisabled()
  758. })
  759. await vi.waitFor(() => {
  760. alert = getByText(outboundForm, sslVerificationWarningText)
  761. expect(alert).toBeInTheDocument()
  762. expect(
  763. getByLabelText(outboundForm, 'SSL verification'),
  764. ).not.toBeDisabled()
  765. })
  766. await view.events.type(getByLabelText(outboundForm, 'Port'), '587')
  767. await vi.waitFor(() => {
  768. expect(
  769. getByLabelText(outboundForm, 'SSL verification'),
  770. ).not.toBeDisabled()
  771. })
  772. await vi.waitFor(() => {
  773. alert = getByText(outboundForm, sslVerificationWarningText)
  774. expect(alert).toBeInTheDocument()
  775. expect(
  776. getByLabelText(outboundForm, 'SSL verification'),
  777. ).not.toBeDisabled()
  778. })
  779. })
  780. it('can go back to channels step', async () => {
  781. const view = await visitView('/guided-setup/manual/channels/email')
  782. const goBackButton = view.getByRole('button', { name: 'Go Back' })
  783. await view.events.click(goBackButton)
  784. await vi.waitFor(() => {
  785. expect(view, 'correctly redirects to channels step').toHaveCurrentUrl(
  786. '/guided-setup/manual/channels',
  787. )
  788. })
  789. })
  790. })
  791. })