PageRenderTime 47ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/src/UnitTests/GitHub.App/Controllers/UIControllerTests.cs

https://gitlab.com/github-cloud-corporation/VisualStudio
C# | 827 lines | 738 code | 74 blank | 15 comment | 17 complexity | d1fa72167463c87ff9f48cdd1ef54f49 MD5 | raw file
  1. using System;
  2. using System.ComponentModel.Composition;
  3. using System.Reactive.Linq;
  4. using GitHub.Controllers;
  5. using GitHub.Models;
  6. using GitHub.Services;
  7. using GitHub.UI;
  8. using NSubstitute;
  9. using Xunit;
  10. using UnitTests;
  11. using GitHub.ViewModels;
  12. using ReactiveUI;
  13. using System.Collections.Generic;
  14. using System.Reactive.Subjects;
  15. using GitHub.Primitives;
  16. using System.ComponentModel;
  17. using System.Collections.ObjectModel;
  18. using GitHub.App.Factories;
  19. public class UIControllerTests
  20. {
  21. public class TheDisposeMethod : TestBaseClass
  22. {
  23. [Fact]
  24. public void WithMultipleCallsDoesNotThrowException()
  25. {
  26. var uiProvider = Substitute.For<IUIProvider>();
  27. var hosts = Substitute.For<IRepositoryHosts>();
  28. var factory = Substitute.For<IUIFactory>();
  29. var cm = Substitutes.ConnectionManager;
  30. var uiController = new UIController(uiProvider, hosts, factory, cm);
  31. uiController.Dispose();
  32. uiController.Dispose();
  33. }
  34. }
  35. public class UIControllerTestBase : TestBaseClass
  36. {
  37. protected void SetupView<VM>(IExportFactoryProvider factory, GitHub.Exports.UIViewType type)
  38. where VM : class, IViewModel
  39. {
  40. IView view;
  41. if (type == GitHub.Exports.UIViewType.PRList)
  42. view = Substitutes.For<IView, IViewFor<VM>, IHasCreationView, IHasDetailView>();
  43. else
  44. view = Substitute.For<IView, IViewFor<VM>>();
  45. view.Done.Returns(new ReplaySubject<ViewWithData>());
  46. view.Cancel.Returns(new ReplaySubject<ViewWithData>());
  47. (view as IHasDetailView)?.Open.Returns(new ReplaySubject<ViewWithData>());
  48. (view as IHasCreationView)?.Create.Returns(new ReplaySubject<ViewWithData>());
  49. var e = new ExportLifetimeContext<IView>(view, () => { });
  50. factory.GetView(type).Returns(e);
  51. }
  52. protected void SetupViewModel<VM>(IExportFactoryProvider factory, GitHub.Exports.UIViewType type)
  53. where VM : class, IViewModel
  54. {
  55. var v = Substitute.For<VM, INotifyPropertyChanged>();
  56. var e = new ExportLifetimeContext<IViewModel>(v, () => { });
  57. factory.GetViewModel(type).Returns(e);
  58. }
  59. protected void RaisePropertyChange(object obj, string prop)
  60. {
  61. (obj as INotifyPropertyChanged).PropertyChanged += Raise.Event<PropertyChangedEventHandler>(new PropertyChangedEventArgs(prop));
  62. }
  63. protected IUIFactory SetupFactory(IServiceProvider provider)
  64. {
  65. var factory = provider.GetExportFactoryProvider();
  66. SetupViewModel<ILoginControlViewModel>(factory, GitHub.Exports.UIViewType.Login);
  67. SetupViewModel<ITwoFactorDialogViewModel>(factory, GitHub.Exports.UIViewType.TwoFactor);
  68. SetupViewModel<IRepositoryCloneViewModel>(factory, GitHub.Exports.UIViewType.Clone);
  69. SetupViewModel<IRepositoryCreationViewModel>(factory, GitHub.Exports.UIViewType.Create);
  70. SetupViewModel<IRepositoryPublishViewModel>(factory, GitHub.Exports.UIViewType.Publish);
  71. SetupViewModel<IPullRequestListViewModel>(factory, GitHub.Exports.UIViewType.PRList);
  72. SetupViewModel<IPullRequestDetailViewModel>(factory, GitHub.Exports.UIViewType.PRDetail);
  73. SetupViewModel<IPullRequestCreationViewModel>(factory, GitHub.Exports.UIViewType.PRCreation);
  74. SetupViewModel<IGistCreationViewModel>(factory, GitHub.Exports.UIViewType.Gist);
  75. SetupViewModel<ILogoutRequiredViewModel>(factory, GitHub.Exports.UIViewType.LogoutRequired);
  76. SetupView<ILoginControlViewModel>(factory, GitHub.Exports.UIViewType.Login);
  77. SetupView<ITwoFactorDialogViewModel>(factory, GitHub.Exports.UIViewType.TwoFactor);
  78. SetupView<IRepositoryCloneViewModel>(factory, GitHub.Exports.UIViewType.Clone);
  79. SetupView<IRepositoryCreationViewModel>(factory, GitHub.Exports.UIViewType.Create);
  80. SetupView<IRepositoryPublishViewModel>(factory, GitHub.Exports.UIViewType.Publish);
  81. SetupView<IPullRequestListViewModel>(factory, GitHub.Exports.UIViewType.PRList);
  82. SetupView<IPullRequestDetailViewModel>(factory, GitHub.Exports.UIViewType.PRDetail);
  83. SetupView<IPullRequestCreationViewModel>(factory, GitHub.Exports.UIViewType.PRCreation);
  84. SetupView<IGistCreationViewModel>(factory, GitHub.Exports.UIViewType.Gist);
  85. SetupView<ILogoutRequiredViewModel>(factory, GitHub.Exports.UIViewType.LogoutRequired);
  86. return new UIFactory(factory);
  87. }
  88. protected IConnection SetupConnection(IServiceProvider provider, IRepositoryHosts hosts,
  89. IRepositoryHost host, bool loggedIn = true, bool supportsGist = true)
  90. {
  91. var connection = provider.GetConnection();
  92. connection.Login().Returns(Observable.Return(connection));
  93. hosts.LookupHost(connection.HostAddress).Returns(host);
  94. host.IsLoggedIn.Returns(loggedIn);
  95. host.SupportsGist.Returns(supportsGist);
  96. return connection;
  97. }
  98. protected void TriggerCancel(IView view)
  99. {
  100. ((ReplaySubject<ViewWithData>)view.Cancel).OnNext(null);
  101. }
  102. protected void TriggerDone(IView view)
  103. {
  104. ((ReplaySubject<ViewWithData>)view.Done).OnNext(null);
  105. }
  106. }
  107. public class AuthFlow : UIControllerTestBase
  108. {
  109. [Fact]
  110. public void RunningNonAuthFlowWithoutBeingLoggedInRunsAuthFlow()
  111. {
  112. var provider = Substitutes.GetFullyMockedServiceProvider();
  113. var hosts = provider.GetRepositoryHosts();
  114. var factory = SetupFactory(provider);
  115. var cm = provider.GetConnectionManager();
  116. var cons = new ObservableCollection<IConnection>();
  117. cm.Connections.Returns(cons);
  118. using (var uiController = new UIController((IUIProvider)provider, hosts, factory, cm))
  119. {
  120. var count = 0;
  121. var flow = uiController.SelectFlow(UIControllerFlow.Clone);
  122. flow.Subscribe(data =>
  123. {
  124. var uc = data.View;
  125. switch (++count)
  126. {
  127. case 1:
  128. Assert.IsAssignableFrom<IViewFor<ILoginControlViewModel>>(uc);
  129. TriggerCancel(uc);
  130. break;
  131. }
  132. });
  133. uiController.Start(null);
  134. Assert.Equal(1, count);
  135. Assert.True(uiController.IsStopped);
  136. }
  137. }
  138. [Fact]
  139. public void RunningNonAuthFlowWhenLoggedInRunsNonAuthFlow()
  140. {
  141. var provider = Substitutes.GetFullyMockedServiceProvider();
  142. var hosts = provider.GetRepositoryHosts();
  143. var factory = SetupFactory(provider);
  144. var cm = provider.GetConnectionManager();
  145. var cons = new ObservableCollection<IConnection>();
  146. cm.Connections.Returns(cons);
  147. // simulate being logged in
  148. cons.Add(SetupConnection(provider, hosts, hosts.GitHubHost));
  149. using (var uiController = new UIController((IUIProvider)provider, hosts, factory, cm))
  150. {
  151. var count = 0;
  152. var flow = uiController.SelectFlow(UIControllerFlow.Clone);
  153. flow.Subscribe(data =>
  154. {
  155. var uc = data.View;
  156. switch (++count)
  157. {
  158. case 1:
  159. Assert.IsAssignableFrom<IViewFor<IRepositoryCloneViewModel>>(uc);
  160. TriggerCancel(uc);
  161. break;
  162. }
  163. });
  164. uiController.Start(null);
  165. Assert.Equal(1, count);
  166. Assert.True(uiController.IsStopped);
  167. }
  168. }
  169. [Fact]
  170. public void RunningAuthFlowWithoutBeingLoggedInRunsAuthFlow()
  171. {
  172. var provider = Substitutes.GetFullyMockedServiceProvider();
  173. var hosts = provider.GetRepositoryHosts();
  174. var factory = SetupFactory(provider);
  175. var cm = provider.GetConnectionManager();
  176. var cons = new ObservableCollection<IConnection>();
  177. cm.Connections.Returns(cons);
  178. using (var uiController = new UIController((IUIProvider)provider, hosts, factory, cm))
  179. {
  180. var count = 0;
  181. var flow = uiController.SelectFlow(UIControllerFlow.Authentication);
  182. flow.Subscribe(data =>
  183. {
  184. var uc = data.View;
  185. switch (++count)
  186. {
  187. case 1:
  188. Assert.IsAssignableFrom<IViewFor<ILoginControlViewModel>>(uc);
  189. TriggerCancel(uc);
  190. break;
  191. }
  192. });
  193. uiController.Start(null);
  194. Assert.Equal(1, count);
  195. Assert.True(uiController.IsStopped);
  196. }
  197. }
  198. [Fact]
  199. public void RunningAuthFlowWhenLoggedInRunsAuthFlow()
  200. {
  201. var provider = Substitutes.GetFullyMockedServiceProvider();
  202. var hosts = provider.GetRepositoryHosts();
  203. var factory = SetupFactory(provider);
  204. var cm = provider.GetConnectionManager();
  205. // simulate being logged in
  206. var host = hosts.GitHubHost;
  207. var connection = SetupConnection(provider, hosts, host);
  208. var cons = new ObservableCollection<IConnection> { connection };
  209. cm.Connections.Returns(cons);
  210. using (var uiController = new UIController((IUIProvider)provider, hosts, factory, cm))
  211. {
  212. var count = 0;
  213. var flow = uiController.SelectFlow(UIControllerFlow.Authentication);
  214. flow.Subscribe(data =>
  215. {
  216. var uc = data.View;
  217. switch (++count)
  218. {
  219. case 1:
  220. Assert.IsAssignableFrom<IViewFor<ILoginControlViewModel>>(uc);
  221. TriggerCancel(uc);
  222. break;
  223. }
  224. });
  225. uiController.Start(null);
  226. Assert.Equal(1, count);
  227. Assert.True(uiController.IsStopped);
  228. }
  229. }
  230. [Fact]
  231. public void AuthFlowWithout2FA()
  232. {
  233. var provider = Substitutes.GetFullyMockedServiceProvider();
  234. var hosts = provider.GetRepositoryHosts();
  235. var factory = SetupFactory(provider);
  236. var cm = provider.GetConnectionManager();
  237. var cons = new ObservableCollection<IConnection>();
  238. cm.Connections.Returns(cons);
  239. using (var uiController = new UIController((IUIProvider)provider, hosts, factory, cm))
  240. {
  241. var count = 0;
  242. var flow = uiController.SelectFlow(UIControllerFlow.Clone);
  243. flow.Subscribe(data =>
  244. {
  245. var uc = data.View;
  246. switch (++count)
  247. {
  248. case 1:
  249. Assert.IsAssignableFrom<IViewFor<ILoginControlViewModel>>(uc);
  250. // login
  251. cons.Add(SetupConnection(provider, hosts, hosts.GitHubHost));
  252. TriggerDone(uc);
  253. break;
  254. case 2:
  255. Assert.IsAssignableFrom<IViewFor<IRepositoryCloneViewModel>>(uc);
  256. TriggerCancel(uc);
  257. break;
  258. }
  259. });
  260. uiController.Start(null);
  261. Assert.Equal(2, count);
  262. Assert.True(uiController.IsStopped);
  263. }
  264. }
  265. [Fact]
  266. public void AuthFlowWith2FA()
  267. {
  268. var provider = Substitutes.GetFullyMockedServiceProvider();
  269. var hosts = provider.GetRepositoryHosts();
  270. var factory = SetupFactory(provider);
  271. var cm = provider.GetConnectionManager();
  272. var cons = new ObservableCollection<IConnection>();
  273. cm.Connections.Returns(cons);
  274. using (var uiController = new UIController((IUIProvider)provider, hosts, factory, cm))
  275. {
  276. var count = 0;
  277. var flow = uiController.SelectFlow(UIControllerFlow.Clone);
  278. flow.Subscribe(data =>
  279. {
  280. var uc = data.View;
  281. switch (++count)
  282. {
  283. case 1:
  284. Assert.IsAssignableFrom<IViewFor<ILoginControlViewModel>>(uc);
  285. var vm = factory.CreateViewAndViewModel(GitHub.Exports.UIViewType.TwoFactor).ViewModel;
  286. vm.IsShowing.Returns(true);
  287. RaisePropertyChange(vm, "IsShowing");
  288. break;
  289. case 2:
  290. Assert.IsAssignableFrom<IViewFor<ITwoFactorDialogViewModel>>(uc);
  291. // login
  292. cons.Add(SetupConnection(provider, hosts, hosts.GitHubHost));
  293. // continue by triggering done on login view
  294. var v = factory.CreateViewAndViewModel(GitHub.Exports.UIViewType.Login).View;
  295. TriggerDone(v);
  296. break;
  297. case 3:
  298. Assert.IsAssignableFrom<IViewFor<IRepositoryCloneViewModel>>(uc);
  299. TriggerCancel(uc);
  300. break;
  301. }
  302. });
  303. uiController.Start(null);
  304. Assert.Equal(3, count);
  305. Assert.True(uiController.IsStopped);
  306. }
  307. }
  308. [Fact]
  309. public void BackAndForth()
  310. {
  311. var provider = Substitutes.GetFullyMockedServiceProvider();
  312. var hosts = provider.GetRepositoryHosts();
  313. var factory = SetupFactory(provider);
  314. var cm = provider.GetConnectionManager();
  315. var cons = new ObservableCollection<IConnection>();
  316. cm.Connections.Returns(cons);
  317. using (var uiController = new UIController((IUIProvider)provider, hosts, factory, cm))
  318. {
  319. var count = 0;
  320. var flow = uiController.SelectFlow(UIControllerFlow.Clone);
  321. flow.Subscribe(data =>
  322. {
  323. var uc = data.View;
  324. switch (++count)
  325. {
  326. case 1: {
  327. Assert.IsAssignableFrom<IViewFor<ILoginControlViewModel>>(uc);
  328. var vm = factory.CreateViewAndViewModel(GitHub.Exports.UIViewType.TwoFactor).ViewModel;
  329. vm.IsShowing.Returns(true);
  330. RaisePropertyChange(vm, "IsShowing");
  331. break;
  332. }
  333. case 2: {
  334. Assert.IsAssignableFrom<IViewFor<ITwoFactorDialogViewModel>>(uc);
  335. var vm = factory.CreateViewAndViewModel(GitHub.Exports.UIViewType.TwoFactor).ViewModel;
  336. vm.IsShowing.Returns(false);
  337. RaisePropertyChange(vm, "IsShowing");
  338. TriggerCancel(uc);
  339. break;
  340. }
  341. case 3: {
  342. Assert.IsAssignableFrom<IViewFor<ILoginControlViewModel>>(uc);
  343. var vm = factory.CreateViewAndViewModel(GitHub.Exports.UIViewType.TwoFactor).ViewModel;
  344. vm.IsShowing.Returns(true);
  345. RaisePropertyChange(vm, "IsShowing");
  346. break;
  347. }
  348. case 4: {
  349. Assert.IsAssignableFrom<IViewFor<ITwoFactorDialogViewModel>>(uc);
  350. // login
  351. cons.Add(SetupConnection(provider, hosts, hosts.GitHubHost));
  352. var v = factory.CreateViewAndViewModel(GitHub.Exports.UIViewType.Login).View;
  353. TriggerDone(v);
  354. break;
  355. }
  356. case 5: {
  357. Assert.IsAssignableFrom<IViewFor<IRepositoryCloneViewModel>>(uc);
  358. uiController.Stop();
  359. break;
  360. }
  361. }
  362. });
  363. uiController.Start(null);
  364. Assert.Equal(5, count);
  365. Assert.True(uiController.IsStopped);
  366. }
  367. }
  368. }
  369. public class CloneFlow : UIControllerTestBase
  370. {
  371. [Fact]
  372. public void Flow()
  373. {
  374. var provider = Substitutes.GetFullyMockedServiceProvider();
  375. var hosts = provider.GetRepositoryHosts();
  376. var factory = SetupFactory(provider);
  377. var cm = provider.GetConnectionManager();
  378. var cons = new ObservableCollection<IConnection>();
  379. cm.Connections.Returns(cons);
  380. // simulate being logged in
  381. cons.Add(SetupConnection(provider, hosts, hosts.GitHubHost));
  382. using (var uiController = new UIController((IUIProvider)provider, hosts, factory, cm))
  383. {
  384. var count = 0;
  385. var flow = uiController.SelectFlow(UIControllerFlow.Clone);
  386. flow.Subscribe(data =>
  387. {
  388. var uc = data.View;
  389. switch (++count)
  390. {
  391. case 1:
  392. Assert.IsAssignableFrom<IViewFor<IRepositoryCloneViewModel>>(uc);
  393. TriggerDone(uc);
  394. break;
  395. }
  396. });
  397. uiController.Start(null);
  398. Assert.Equal(1, count);
  399. Assert.True(uiController.IsStopped);
  400. }
  401. }
  402. }
  403. public class CreateFlow : UIControllerTestBase
  404. {
  405. [Fact]
  406. public void Flow()
  407. {
  408. var provider = Substitutes.GetFullyMockedServiceProvider();
  409. var hosts = provider.GetRepositoryHosts();
  410. var factory = SetupFactory(provider);
  411. var cm = provider.GetConnectionManager();
  412. var cons = new ObservableCollection<IConnection>();
  413. cm.Connections.Returns(cons);
  414. // simulate being logged in
  415. cons.Add(SetupConnection(provider, hosts, hosts.GitHubHost));
  416. using (var uiController = new UIController((IUIProvider)provider, hosts, factory, cm))
  417. {
  418. var count = 0;
  419. var flow = uiController.SelectFlow(UIControllerFlow.Create);
  420. flow.Subscribe(data =>
  421. {
  422. var uc = data.View;
  423. switch (++count)
  424. {
  425. case 1:
  426. Assert.IsAssignableFrom<IViewFor<IRepositoryCreationViewModel>>(uc);
  427. TriggerDone(uc);
  428. break;
  429. }
  430. });
  431. uiController.Start(null);
  432. Assert.Equal(1, count);
  433. Assert.True(uiController.IsStopped);
  434. }
  435. }
  436. }
  437. public class PublishFlow : UIControllerTestBase
  438. {
  439. [Fact]
  440. public void FlowWithConnection()
  441. {
  442. var provider = Substitutes.GetFullyMockedServiceProvider();
  443. var hosts = provider.GetRepositoryHosts();
  444. var factory = SetupFactory(provider);
  445. var cm = provider.GetConnectionManager();
  446. var cons = new ObservableCollection<IConnection>();
  447. cm.Connections.Returns(cons);
  448. var connection = SetupConnection(provider, hosts, hosts.GitHubHost);
  449. // simulate being logged in
  450. cons.Add(connection);
  451. using (var uiController = new UIController((IUIProvider)provider, hosts, factory, cm))
  452. {
  453. var count = 0;
  454. var flow = uiController.SelectFlow(UIControllerFlow.Publish);
  455. flow.Subscribe(data =>
  456. {
  457. var uc = data.View;
  458. switch (++count)
  459. {
  460. case 1:
  461. Assert.IsAssignableFrom<IViewFor<IRepositoryPublishViewModel>>(uc);
  462. ((IUIProvider)provider).Received().AddService(uiController, connection);
  463. TriggerDone(uc);
  464. break;
  465. }
  466. });
  467. uiController.Start(connection);
  468. Assert.Equal(1, count);
  469. Assert.True(uiController.IsStopped);
  470. }
  471. }
  472. [Fact]
  473. public void FlowWithoutConnection()
  474. {
  475. var provider = Substitutes.GetFullyMockedServiceProvider();
  476. var hosts = provider.GetRepositoryHosts();
  477. var factory = SetupFactory(provider);
  478. var cm = provider.GetConnectionManager();
  479. var cons = new ObservableCollection<IConnection>();
  480. cm.Connections.Returns(cons);
  481. var connection = SetupConnection(provider, hosts, hosts.GitHubHost);
  482. // simulate being logged in
  483. cons.Add(connection);
  484. using (var uiController = new UIController((IUIProvider)provider, hosts, factory, cm))
  485. {
  486. var count = 0;
  487. var flow = uiController.SelectFlow(UIControllerFlow.Publish);
  488. flow.Subscribe(data =>
  489. {
  490. var uc = data.View;
  491. switch (++count)
  492. {
  493. case 1:
  494. Assert.IsAssignableFrom<IViewFor<IRepositoryPublishViewModel>>(uc);
  495. ((IUIProvider)provider).Received().AddService(uiController, connection);
  496. TriggerDone(uc);
  497. break;
  498. }
  499. });
  500. uiController.Start(null);
  501. Assert.Equal(1, count);
  502. Assert.True(uiController.IsStopped);
  503. }
  504. }
  505. }
  506. public class PullRequestsFlow : UIControllerTestBase
  507. {
  508. [Fact]
  509. public void Flow()
  510. {
  511. var provider = Substitutes.GetFullyMockedServiceProvider();
  512. var hosts = provider.GetRepositoryHosts();
  513. var factory = SetupFactory(provider);
  514. var cm = provider.GetConnectionManager();
  515. var cons = new ObservableCollection<IConnection>();
  516. cm.Connections.Returns(cons);
  517. // simulate being logged in
  518. cons.Add(SetupConnection(provider, hosts, hosts.GitHubHost));
  519. using (var uiController = new UIController((IUIProvider)provider, hosts, factory, cm))
  520. {
  521. var count = 0;
  522. bool? success = null;
  523. var flow = uiController.SelectFlow(UIControllerFlow.PullRequests);
  524. uiController.ListenToCompletionState()
  525. .Subscribe(s =>
  526. {
  527. success = s;
  528. });
  529. flow.Subscribe(data =>
  530. {
  531. var uc = data.View;
  532. switch (++count)
  533. {
  534. case 1:
  535. Assert.IsAssignableFrom<IViewFor<IPullRequestListViewModel>>(uc);
  536. ((ReplaySubject<ViewWithData>)((IHasDetailView)uc).Open).OnNext(
  537. new ViewWithData(UIControllerFlow.PullRequests) { ViewType = GitHub.Exports.UIViewType.PRDetail, Data = 1 });
  538. break;
  539. case 2:
  540. Assert.IsAssignableFrom<IViewFor<IPullRequestDetailViewModel>>(uc);
  541. TriggerDone(uc);
  542. break;
  543. case 3:
  544. Assert.IsAssignableFrom<IViewFor<IPullRequestListViewModel>>(uc);
  545. ((ReplaySubject<ViewWithData>)((IHasDetailView)uc).Open).OnNext(
  546. new ViewWithData(UIControllerFlow.PullRequests) { ViewType = GitHub.Exports.UIViewType.PRDetail, Data = 1 });
  547. break;
  548. case 4:
  549. Assert.IsAssignableFrom<IViewFor<IPullRequestDetailViewModel>>(uc);
  550. TriggerCancel(uc);
  551. break;
  552. case 5:
  553. Assert.IsAssignableFrom<IViewFor<IPullRequestListViewModel>>(uc);
  554. ((ReplaySubject<ViewWithData>)((IHasCreationView)uc).Create).OnNext(null);
  555. break;
  556. case 6:
  557. Assert.IsAssignableFrom<IViewFor<IPullRequestCreationViewModel>>(uc);
  558. TriggerCancel(uc);
  559. break;
  560. case 7:
  561. Assert.IsAssignableFrom<IViewFor<IPullRequestListViewModel>>(uc);
  562. ((ReplaySubject<ViewWithData>)((IHasCreationView)uc).Create).OnNext(null);
  563. break;
  564. case 8:
  565. Assert.IsAssignableFrom<IViewFor<IPullRequestCreationViewModel>>(uc);
  566. TriggerDone(uc);
  567. break;
  568. case 9:
  569. Assert.IsAssignableFrom<IViewFor<IPullRequestListViewModel>>(uc);
  570. TriggerCancel(uc);
  571. break;
  572. }
  573. });
  574. uiController.Start(null);
  575. Assert.Equal(9, count);
  576. Assert.True(uiController.IsStopped);
  577. Assert.True(success.HasValue);
  578. Assert.False(success);
  579. }
  580. }
  581. [Fact]
  582. public void ShuttingDown()
  583. {
  584. var provider = Substitutes.GetFullyMockedServiceProvider();
  585. var hosts = provider.GetRepositoryHosts();
  586. var factory = SetupFactory(provider);
  587. var cm = provider.GetConnectionManager();
  588. var cons = new ObservableCollection<IConnection>();
  589. cm.Connections.Returns(cons);
  590. // simulate being logged in
  591. cons.Add(SetupConnection(provider, hosts, hosts.GitHubHost));
  592. using (var uiController = new UIController((IUIProvider)provider, hosts, factory, cm))
  593. {
  594. var count = 0;
  595. bool? success = null;
  596. var flow = uiController.SelectFlow(UIControllerFlow.PullRequests);
  597. uiController.ListenToCompletionState()
  598. .Subscribe(s =>
  599. {
  600. success = s;
  601. Assert.Equal(4, count);
  602. count++;
  603. });
  604. flow.Subscribe(data =>
  605. {
  606. var uc = data.View;
  607. switch (++count)
  608. {
  609. case 1:
  610. Assert.IsAssignableFrom<IViewFor<IPullRequestListViewModel>>(uc);
  611. ((ReplaySubject<ViewWithData>)((IHasDetailView)uc).Open).OnNext(
  612. new ViewWithData(UIControllerFlow.PullRequests) { ViewType = GitHub.Exports.UIViewType.PRDetail, Data = 1 });
  613. break;
  614. case 2:
  615. Assert.IsAssignableFrom<IViewFor<IPullRequestDetailViewModel>>(uc);
  616. TriggerDone(uc);
  617. break;
  618. case 3:
  619. Assert.IsAssignableFrom<IViewFor<IPullRequestListViewModel>>(uc);
  620. ((ReplaySubject<ViewWithData>)((IHasDetailView)uc).Open).OnNext(
  621. new ViewWithData(UIControllerFlow.PullRequests) { ViewType = GitHub.Exports.UIViewType.PRDetail, Data = 1 });
  622. break;
  623. case 4:
  624. Assert.IsAssignableFrom<IViewFor<IPullRequestDetailViewModel>>(uc);
  625. uiController.Stop();
  626. break;
  627. }
  628. }, () =>
  629. {
  630. Assert.Equal(5, count);
  631. count++;
  632. });
  633. uiController.Start(null);
  634. Assert.Equal(6, count);
  635. Assert.True(uiController.IsStopped);
  636. Assert.True(success.HasValue);
  637. Assert.True(success);
  638. }
  639. }
  640. }
  641. public class GistFlow : UIControllerTestBase
  642. {
  643. [Fact]
  644. public void ShowingGistDialogWhenGistNotSupportedShowsLogoutDialog()
  645. {
  646. var provider = Substitutes.GetFullyMockedServiceProvider();
  647. var hosts = provider.GetRepositoryHosts();
  648. var factory = SetupFactory(provider);
  649. var cm = provider.GetConnectionManager();
  650. var cons = new ObservableCollection<IConnection>();
  651. cm.Connections.Returns(cons);
  652. var host = hosts.GitHubHost;
  653. // simulate being logged in
  654. cons.Add(SetupConnection(provider, hosts, host, true, false));
  655. using (var uiController = new UIController((IUIProvider)provider, hosts, factory, cm))
  656. {
  657. var count = 0;
  658. bool? success = null;
  659. var flow = uiController.SelectFlow(UIControllerFlow.Gist);
  660. uiController.ListenToCompletionState()
  661. .Subscribe(s =>
  662. {
  663. success = s;
  664. Assert.Equal(3, count);
  665. count++;
  666. });
  667. flow.Subscribe(data =>
  668. {
  669. var uc = data.View;
  670. switch (++count)
  671. {
  672. case 1:
  673. Assert.IsAssignableFrom<IViewFor<ILogoutRequiredViewModel>>(uc);
  674. host.IsLoggedIn.Returns(false);
  675. TriggerDone(uc);
  676. break;
  677. case 2:
  678. Assert.IsAssignableFrom<IViewFor<ILoginControlViewModel>>(uc);
  679. // login
  680. host.IsLoggedIn.Returns(true);
  681. host.SupportsGist.Returns(true);
  682. TriggerDone(uc);
  683. break;
  684. case 3:
  685. Assert.IsAssignableFrom<IViewFor<IGistCreationViewModel>>(uc);
  686. TriggerDone(uc);
  687. break;
  688. default:
  689. Assert.True(false, "Received more views than expected");
  690. break;
  691. }
  692. }, () =>
  693. {
  694. Assert.Equal(4, count);
  695. count++;
  696. });
  697. uiController.Start(null);
  698. Assert.Equal(5, count);
  699. Assert.True(uiController.IsStopped);
  700. Assert.True(success.HasValue);
  701. Assert.True(success);
  702. }
  703. }
  704. [Fact]
  705. public void ShowingGistDialogWhenGistSupportedShowsGistDialog()
  706. {
  707. var provider = Substitutes.GetFullyMockedServiceProvider();
  708. var hosts = provider.GetRepositoryHosts();
  709. var factory = SetupFactory(provider);
  710. var cm = provider.GetConnectionManager();
  711. var cons = new ObservableCollection<IConnection>();
  712. cm.Connections.Returns(cons);
  713. // simulate being logged in
  714. cons.Add(SetupConnection(provider, hosts, hosts.GitHubHost, true, true));
  715. using (var uiController = new UIController((IUIProvider)provider, hosts, factory, cm))
  716. {
  717. var count = 0;
  718. bool? success = null;
  719. var flow = uiController.SelectFlow(UIControllerFlow.Gist);
  720. uiController.ListenToCompletionState()
  721. .Subscribe(s =>
  722. {
  723. success = s;
  724. Assert.Equal(1, count);
  725. count++;
  726. });
  727. flow.Subscribe(data =>
  728. {
  729. var uc = data.View;
  730. switch (++count)
  731. {
  732. case 1:
  733. Assert.IsAssignableFrom<IViewFor<IGistCreationViewModel>>(uc);
  734. TriggerDone(uc);
  735. break;
  736. default:
  737. Assert.True(false, "Received more views than expected");
  738. break;
  739. }
  740. }, () =>
  741. {
  742. Assert.Equal(2, count);
  743. count++;
  744. });
  745. uiController.Start(null);
  746. Assert.Equal(3, count);
  747. Assert.True(uiController.IsStopped);
  748. Assert.True(success.HasValue);
  749. Assert.True(success);
  750. }
  751. }
  752. }
  753. }