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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999
  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 getNode('channel-email-inbound-messages')?.settled
  562. await view.events.click(
  563. view.getByRole('button', {
  564. name: 'Continue',
  565. }),
  566. )
  567. const outboundForm = view.getByTestId('channel-email-outbound')
  568. await view.events.type(getByLabelText(outboundForm, 'Port'), '25')
  569. mockChannelEmailValidateConfigurationOutboundMutation({
  570. channelEmailValidateConfigurationOutbound: {
  571. success: true,
  572. },
  573. })
  574. mockChannelEmailValidateConfigurationRoundtripMutation({
  575. channelEmailValidateConfigurationRoundtrip: {
  576. success: true,
  577. },
  578. })
  579. mockChannelEmailAddMutation({
  580. channelEmailAdd: {
  581. channel: {
  582. options: {},
  583. group: {
  584. id: 'gid://zammad/Group/1',
  585. },
  586. },
  587. },
  588. })
  589. await view.events.click(
  590. view.getByRole('button', {
  591. name: 'Save and Continue',
  592. }),
  593. )
  594. const calls = await waitForChannelEmailAddMutationCalls()
  595. expect(calls.at(-1)?.variables).toEqual({
  596. input: {
  597. emailAddress: 'zammad@mail.test.dc.zammad.com',
  598. emailRealname: 'Zammad Helpdesk',
  599. inboundConfiguration: {
  600. adapter: 'imap',
  601. folder: '',
  602. host: 'mail.test.dc.zammad.com',
  603. keepOnServer: false,
  604. password: 'zammad',
  605. port: 993,
  606. ssl: 'ssl',
  607. sslVerify: true,
  608. user: 'zammad@mail.test.dc.zammad.com',
  609. archive: true,
  610. archiveBefore: '2025-01-01T00:00:00Z',
  611. archiveStateId: 4,
  612. },
  613. outboundConfiguration: {
  614. adapter: 'smtp',
  615. host: 'mail.test.dc.zammad.com',
  616. password: 'zammad',
  617. port: 25,
  618. sslVerify: false,
  619. user: 'zammad@mail.test.dc.zammad.com',
  620. },
  621. },
  622. })
  623. await vi.waitFor(() => {
  624. expect(view).toHaveCurrentUrl('/guided-setup/manual/invite')
  625. })
  626. })
  627. it('can show warning when SSL/STARTTLS is used and SSL verification is turned off in inbound form', async () => {
  628. const view = await visitView('/guided-setup/manual/channels/email')
  629. const accountForm = view.getByTestId('channel-email-account')
  630. await view.events.type(
  631. getByLabelText(accountForm, 'Full name'),
  632. 'Zammad Helpdesk',
  633. )
  634. await view.events.type(
  635. getByLabelText(accountForm, 'Email address'),
  636. 'zammad@mail.test.dc.zammad.com',
  637. )
  638. await view.events.type(getByLabelText(accountForm, 'Password'), 'zammad')
  639. mockChannelEmailGuessConfigurationMutation({
  640. channelEmailGuessConfiguration: {
  641. result: {
  642. inboundConfiguration: null,
  643. outboundConfiguration: null,
  644. },
  645. },
  646. })
  647. await view.events.click(
  648. view.getByRole('button', {
  649. name: 'Connect and Continue',
  650. }),
  651. )
  652. const inboundForm = view.getByTestId('channel-email-inbound')
  653. let alert = queryByText(inboundForm, sslVerificationWarningText)
  654. expect(alert).not.toBeInTheDocument()
  655. await view.events.click(getByLabelText(inboundForm, 'SSL verification'))
  656. alert = getByText(inboundForm, sslVerificationWarningText)
  657. expect(alert?.role).toBe('alert')
  658. await view.events.click(getByLabelText(inboundForm, 'SSL verification'))
  659. expect(alert).not.toBeInTheDocument()
  660. await view.events.click(getByLabelText(inboundForm, 'SSL verification'))
  661. alert = getByText(inboundForm, sslVerificationWarningText)
  662. expect(alert).toBeInTheDocument()
  663. const sslField = view.getByLabelText('SSL/STARTTLS')
  664. await view.events.click(sslField)
  665. await view.events.click(view.getAllByRole('option')[0])
  666. expect(alert).not.toBeInTheDocument()
  667. await view.events.click(sslField)
  668. await view.events.click(view.getAllByRole('option')[2])
  669. alert = queryByText(inboundForm, sslVerificationWarningText)
  670. expect(alert).not.toBeInTheDocument()
  671. await view.events.click(getByLabelText(inboundForm, 'SSL verification'))
  672. alert = getByText(inboundForm, sslVerificationWarningText)
  673. expect(alert).toBeInTheDocument()
  674. })
  675. it('can show warning when secure port is used and SSL verification is turned off in outbound form', async () => {
  676. const view = await visitView('/guided-setup/manual/channels/email')
  677. const accountForm = view.getByTestId('channel-email-account')
  678. await view.events.type(
  679. getByLabelText(accountForm, 'Full name'),
  680. 'Zammad Helpdesk',
  681. )
  682. await view.events.type(
  683. getByLabelText(accountForm, 'Email address'),
  684. 'zammad@mail.test.dc.zammad.com',
  685. )
  686. await view.events.type(getByLabelText(accountForm, 'Password'), 'zammad')
  687. mockChannelEmailGuessConfigurationMutation({
  688. channelEmailGuessConfiguration: {
  689. result: {
  690. inboundConfiguration: null,
  691. outboundConfiguration: null,
  692. },
  693. },
  694. })
  695. await view.events.click(
  696. view.getByRole('button', {
  697. name: 'Connect and Continue',
  698. }),
  699. )
  700. const inboundForm = view.getByTestId('channel-email-inbound')
  701. await view.events.type(
  702. getByLabelText(inboundForm, 'Host'),
  703. 'mail.test.dc.zammad.com',
  704. )
  705. await getNode('channel-email-inbound')?.settled
  706. mockChannelEmailValidateConfigurationInboundMutation({
  707. channelEmailValidateConfigurationInbound: {
  708. success: true,
  709. mailboxStats: {
  710. contentMessages: 0,
  711. },
  712. },
  713. })
  714. await view.events.click(
  715. view.getByRole('button', {
  716. name: 'Continue',
  717. }),
  718. )
  719. const outboundForm = view.getByTestId('channel-email-outbound')
  720. let alert = queryByText(outboundForm, sslVerificationWarningText)
  721. expect(alert).not.toBeInTheDocument()
  722. expect(
  723. getByLabelText(outboundForm, 'SSL verification'),
  724. ).not.toBeDisabled()
  725. await view.events.click(getByLabelText(outboundForm, 'SSL verification'))
  726. alert = getByText(outboundForm, sslVerificationWarningText)
  727. expect(alert?.role).toBe('alert')
  728. await view.events.click(getByLabelText(outboundForm, 'SSL verification'))
  729. expect(alert).not.toBeInTheDocument()
  730. await view.events.click(getByLabelText(outboundForm, 'SSL verification'))
  731. alert = getByText(outboundForm, sslVerificationWarningText)
  732. expect(alert).toBeInTheDocument()
  733. await view.events.type(getByLabelText(outboundForm, 'Port'), '25')
  734. await vi.waitFor(() => {
  735. expect(alert).not.toBeInTheDocument()
  736. expect(getByLabelText(outboundForm, 'SSL verification')).toBeDisabled()
  737. })
  738. await view.events.clear(getByLabelText(outboundForm, 'Port'))
  739. await vi.waitFor(() => {
  740. expect(
  741. getByLabelText(outboundForm, 'SSL verification'),
  742. ).not.toBeDisabled()
  743. })
  744. await view.events.click(getByLabelText(outboundForm, 'SSL verification'))
  745. await vi.waitFor(() => {
  746. alert = getByText(outboundForm, sslVerificationWarningText)
  747. expect(alert).toBeInTheDocument()
  748. expect(
  749. getByLabelText(outboundForm, 'SSL verification'),
  750. ).not.toBeDisabled()
  751. })
  752. await view.events.type(getByLabelText(outboundForm, 'Port'), '465')
  753. await vi.waitFor(() => {
  754. expect(
  755. getByLabelText(outboundForm, 'SSL verification'),
  756. ).not.toBeDisabled()
  757. })
  758. await vi.waitFor(() => {
  759. alert = getByText(outboundForm, sslVerificationWarningText)
  760. expect(alert).toBeInTheDocument()
  761. expect(
  762. getByLabelText(outboundForm, 'SSL verification'),
  763. ).not.toBeDisabled()
  764. })
  765. await view.events.type(getByLabelText(outboundForm, 'Port'), '587')
  766. await vi.waitFor(() => {
  767. expect(
  768. getByLabelText(outboundForm, 'SSL verification'),
  769. ).not.toBeDisabled()
  770. })
  771. await vi.waitFor(() => {
  772. alert = getByText(outboundForm, sslVerificationWarningText)
  773. expect(alert).toBeInTheDocument()
  774. expect(
  775. getByLabelText(outboundForm, 'SSL verification'),
  776. ).not.toBeDisabled()
  777. })
  778. })
  779. it('can go back to channels step', async () => {
  780. const view = await visitView('/guided-setup/manual/channels/email')
  781. const goBackButton = view.getByRole('button', { name: 'Go Back' })
  782. await view.events.click(goBackButton)
  783. await vi.waitFor(() => {
  784. expect(view, 'correctly redirects to channels step').toHaveCurrentUrl(
  785. '/guided-setup/manual/channels',
  786. )
  787. })
  788. })
  789. })
  790. })