pane-element-spec.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. const PaneContainer = require('../src/pane-container');
  2. describe('PaneElement', function() {
  3. let [paneElement, container, containerElement, pane] = [];
  4. beforeEach(function() {
  5. spyOn(atom.applicationDelegate, 'open');
  6. container = new PaneContainer({
  7. location: 'center',
  8. config: atom.config,
  9. confirm: atom.confirm.bind(atom),
  10. viewRegistry: atom.views,
  11. applicationDelegate: atom.applicationDelegate
  12. });
  13. containerElement = container.getElement();
  14. pane = container.getActivePane();
  15. paneElement = pane.getElement();
  16. });
  17. describe("when the pane's active status changes", () =>
  18. it('adds or removes the .active class as appropriate', function() {
  19. const pane2 = pane.splitRight();
  20. expect(pane2.isActive()).toBe(true);
  21. expect(paneElement.className).not.toMatch(/active/);
  22. pane.activate();
  23. expect(paneElement.className).toMatch(/active/);
  24. pane2.activate();
  25. expect(paneElement.className).not.toMatch(/active/);
  26. }));
  27. describe('when the active item changes', function() {
  28. it('hides all item elements except the active one', function() {
  29. const item1 = document.createElement('div');
  30. const item2 = document.createElement('div');
  31. const item3 = document.createElement('div');
  32. pane.addItem(item1);
  33. pane.addItem(item2);
  34. pane.addItem(item3);
  35. expect(pane.getActiveItem()).toBe(item1);
  36. expect(item1.parentElement).toBeDefined();
  37. expect(item1.style.display).toBe('');
  38. expect(item2.parentElement).toBeNull();
  39. expect(item3.parentElement).toBeNull();
  40. pane.activateItem(item2);
  41. expect(item2.parentElement).toBeDefined();
  42. expect(item1.style.display).toBe('none');
  43. expect(item2.style.display).toBe('');
  44. expect(item3.parentElement).toBeNull();
  45. pane.activateItem(item3);
  46. expect(item3.parentElement).toBeDefined();
  47. expect(item1.style.display).toBe('none');
  48. expect(item2.style.display).toBe('none');
  49. expect(item3.style.display).toBe('');
  50. });
  51. it('transfers focus to the new item if the previous item was focused', function() {
  52. const item1 = document.createElement('div');
  53. item1.tabIndex = -1;
  54. const item2 = document.createElement('div');
  55. item2.tabIndex = -1;
  56. pane.addItem(item1);
  57. pane.addItem(item2);
  58. jasmine.attachToDOM(paneElement);
  59. paneElement.focus();
  60. expect(document.activeElement).toBe(item1);
  61. pane.activateItem(item2);
  62. expect(document.activeElement).toBe(item2);
  63. });
  64. describe('if the active item is a model object', () =>
  65. it('retrieves the associated view from atom.views and appends it to the itemViews div', function() {
  66. class TestModel {}
  67. atom.views.addViewProvider(TestModel, function(model) {
  68. const view = document.createElement('div');
  69. view.model = model;
  70. return view;
  71. });
  72. const item1 = new TestModel();
  73. const item2 = new TestModel();
  74. pane.addItem(item1);
  75. pane.addItem(item2);
  76. expect(paneElement.itemViews.children[0].model).toBe(item1);
  77. expect(paneElement.itemViews.children[0].style.display).toBe('');
  78. pane.activateItem(item2);
  79. expect(paneElement.itemViews.children[1].model).toBe(item2);
  80. expect(paneElement.itemViews.children[0].style.display).toBe('none');
  81. expect(paneElement.itemViews.children[1].style.display).toBe('');
  82. }));
  83. describe('when the new active implements .getPath()', function() {
  84. it('adds the file path and file name as a data attribute on the pane', function() {
  85. const item1 = document.createElement('div');
  86. item1.getPath = () => '/foo/bar.txt';
  87. const item2 = document.createElement('div');
  88. pane.addItem(item1);
  89. pane.addItem(item2);
  90. expect(paneElement.dataset.activeItemPath).toBe('/foo/bar.txt');
  91. expect(paneElement.dataset.activeItemName).toBe('bar.txt');
  92. pane.activateItem(item2);
  93. expect(paneElement.dataset.activeItemPath).toBeUndefined();
  94. expect(paneElement.dataset.activeItemName).toBeUndefined();
  95. pane.activateItem(item1);
  96. expect(paneElement.dataset.activeItemPath).toBe('/foo/bar.txt');
  97. expect(paneElement.dataset.activeItemName).toBe('bar.txt');
  98. pane.destroyItems();
  99. expect(paneElement.dataset.activeItemPath).toBeUndefined();
  100. expect(paneElement.dataset.activeItemName).toBeUndefined();
  101. });
  102. describe('when the path of the item changes', function() {
  103. let [item1, item2] = [];
  104. beforeEach(function() {
  105. item1 = document.createElement('div');
  106. item1.path = '/foo/bar.txt';
  107. item1.changePathCallbacks = [];
  108. item1.setPath = function(path) {
  109. this.path = path;
  110. for (let callback of Array.from(this.changePathCallbacks)) {
  111. callback();
  112. }
  113. };
  114. item1.getPath = function() {
  115. return this.path;
  116. };
  117. item1.onDidChangePath = function(callback) {
  118. this.changePathCallbacks.push(callback);
  119. return {
  120. dispose: () => {
  121. this.changePathCallbacks = this.changePathCallbacks.filter(
  122. f => f !== callback
  123. );
  124. }
  125. };
  126. };
  127. item2 = document.createElement('div');
  128. pane.addItem(item1);
  129. pane.addItem(item2);
  130. });
  131. it('changes the file path and file name data attributes on the pane if the active item path is changed', function() {
  132. expect(paneElement.dataset.activeItemPath).toBe('/foo/bar.txt');
  133. expect(paneElement.dataset.activeItemName).toBe('bar.txt');
  134. item1.setPath('/foo/bar1.txt');
  135. expect(paneElement.dataset.activeItemPath).toBe('/foo/bar1.txt');
  136. expect(paneElement.dataset.activeItemName).toBe('bar1.txt');
  137. pane.activateItem(item2);
  138. expect(paneElement.dataset.activeItemPath).toBeUndefined();
  139. expect(paneElement.dataset.activeItemName).toBeUndefined();
  140. item1.setPath('/foo/bar2.txt');
  141. expect(paneElement.dataset.activeItemPath).toBeUndefined();
  142. expect(paneElement.dataset.activeItemName).toBeUndefined();
  143. pane.activateItem(item1);
  144. expect(paneElement.dataset.activeItemPath).toBe('/foo/bar2.txt');
  145. expect(paneElement.dataset.activeItemName).toBe('bar2.txt');
  146. });
  147. });
  148. });
  149. });
  150. describe('when an item is removed from the pane', function() {
  151. describe('when the destroyed item is an element', () =>
  152. it('removes the item from the itemViews div', function() {
  153. const item1 = document.createElement('div');
  154. const item2 = document.createElement('div');
  155. pane.addItem(item1);
  156. pane.addItem(item2);
  157. paneElement = pane.getElement();
  158. expect(item1.parentElement).toBe(paneElement.itemViews);
  159. pane.destroyItem(item1);
  160. expect(item1.parentElement).toBeNull();
  161. expect(item2.parentElement).toBe(paneElement.itemViews);
  162. pane.destroyItem(item2);
  163. expect(item2.parentElement).toBeNull();
  164. }));
  165. describe('when the destroyed item is a model', () =>
  166. it("removes the model's associated view", function() {
  167. class TestModel {}
  168. atom.views.addViewProvider(TestModel, function(model) {
  169. const view = document.createElement('div');
  170. model.element = view;
  171. view.model = model;
  172. return view;
  173. });
  174. const item1 = new TestModel();
  175. const item2 = new TestModel();
  176. pane.addItem(item1);
  177. pane.addItem(item2);
  178. expect(item1.element.parentElement).toBe(paneElement.itemViews);
  179. pane.destroyItem(item1);
  180. expect(item1.element.parentElement).toBeNull();
  181. expect(item2.element.parentElement).toBe(paneElement.itemViews);
  182. pane.destroyItem(item2);
  183. expect(item2.element.parentElement).toBeNull();
  184. }));
  185. });
  186. describe('when the pane element is focused', function() {
  187. it('transfers focus to the active view', function() {
  188. const item = document.createElement('div');
  189. item.tabIndex = -1;
  190. pane.activateItem(item);
  191. jasmine.attachToDOM(paneElement);
  192. expect(document.activeElement).toBe(document.body);
  193. paneElement.focus();
  194. expect(document.activeElement).toBe(item);
  195. document.body.focus();
  196. pane.activate();
  197. expect(document.activeElement).toBe(item);
  198. });
  199. it('makes the pane active', function() {
  200. pane.splitRight();
  201. expect(pane.isActive()).toBe(false);
  202. jasmine.attachToDOM(paneElement);
  203. paneElement.focus();
  204. expect(pane.isActive()).toBe(true);
  205. });
  206. it('does not re-activate the pane when focus changes within the pane', function() {
  207. const item = document.createElement('div');
  208. const itemChild = document.createElement('div');
  209. item.tabIndex = -1;
  210. itemChild.tabIndex = -1;
  211. item.appendChild(itemChild);
  212. jasmine.attachToDOM(paneElement);
  213. pane.activateItem(item);
  214. pane.activate();
  215. let activationCount = 0;
  216. pane.onDidActivate(() => activationCount++);
  217. itemChild.focus();
  218. expect(activationCount).toBe(0);
  219. });
  220. });
  221. describe('when the pane element is attached', () =>
  222. it('focuses the pane element if isFocused() returns true on its model', function() {
  223. pane.focus();
  224. jasmine.attachToDOM(paneElement);
  225. expect(document.activeElement).toBe(paneElement);
  226. }));
  227. describe('drag and drop', function() {
  228. const buildDragEvent = function(type, files) {
  229. const dataTransfer = {
  230. files,
  231. data: {},
  232. setData(key, value) {
  233. this.data[key] = value;
  234. },
  235. getData(key) {
  236. return this.data[key];
  237. }
  238. };
  239. const event = new CustomEvent('drop');
  240. event.dataTransfer = dataTransfer;
  241. return event;
  242. };
  243. describe('when a file is dragged to the pane', () =>
  244. it('opens it', function() {
  245. const event = buildDragEvent('drop', [
  246. { path: '/fake1' },
  247. { path: '/fake2' }
  248. ]);
  249. paneElement.dispatchEvent(event);
  250. expect(atom.applicationDelegate.open.callCount).toBe(1);
  251. expect(atom.applicationDelegate.open.argsForCall[0][0]).toEqual({
  252. pathsToOpen: ['/fake1', '/fake2'],
  253. here: true
  254. });
  255. }));
  256. describe('when a non-file is dragged to the pane', () =>
  257. it('does nothing', function() {
  258. const event = buildDragEvent('drop', []);
  259. paneElement.dispatchEvent(event);
  260. expect(atom.applicationDelegate.open).not.toHaveBeenCalled();
  261. }));
  262. });
  263. describe('resize', () =>
  264. it("shrinks independently of its contents' width", function() {
  265. jasmine.attachToDOM(containerElement);
  266. const item = document.createElement('div');
  267. item.style.width = '2000px';
  268. item.style.height = '30px';
  269. paneElement.insertBefore(item, paneElement.children[0]);
  270. paneElement.style.flexGrow = 0.1;
  271. expect(paneElement.getBoundingClientRect().width).toBeGreaterThan(0);
  272. expect(paneElement.getBoundingClientRect().width).toBeLessThan(
  273. item.getBoundingClientRect().width
  274. );
  275. paneElement.style.flexGrow = 0;
  276. expect(paneElement.getBoundingClientRect().width).toBe(0);
  277. }));
  278. });