line-ending-selector-spec.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. const helpers = require('../lib/helpers');
  2. const { TextEditor } = require('atom');
  3. const path = require('path');
  4. describe('line ending selector', () => {
  5. let lineEndingTile;
  6. beforeEach(() => {
  7. jasmine.useRealClock();
  8. waitsForPromise(() => {
  9. return atom.packages.activatePackage('status-bar');
  10. });
  11. waitsForPromise(() => {
  12. return atom.packages.activatePackage('line-ending-selector');
  13. });
  14. waits(1);
  15. runs(() => {
  16. const statusBar = atom.workspace.getFooterPanels()[0].getItem();
  17. lineEndingTile = statusBar.getRightTiles()[0].getItem();
  18. expect(lineEndingTile.element.className).toMatch(/line-ending-tile/);
  19. expect(lineEndingTile.element.textContent).toBe('');
  20. });
  21. });
  22. describe('Commands', () => {
  23. let editor, editorElement;
  24. beforeEach(() => {
  25. waitsForPromise(() => {
  26. return atom.workspace.open(path.join(__dirname, 'fixtures', 'mixed-endings.md')).then(e => {
  27. editor = e;
  28. editorElement = atom.views.getView(editor);
  29. jasmine.attachToDOM(editorElement);
  30. });
  31. });
  32. });
  33. describe('When "line-ending-selector:convert-to-LF" is run', () => {
  34. it('converts the file to LF line endings', () => {
  35. editorElement.focus();
  36. atom.commands.dispatch(
  37. document.activeElement,
  38. 'line-ending-selector:convert-to-LF'
  39. );
  40. expect(editor.getText()).toBe('Hello\nGoodbye\nMixed\n');
  41. });
  42. });
  43. describe('When "line-ending-selector:convert-to-LF" is run', () => {
  44. it('converts the file to CRLF line endings', () => {
  45. editorElement.focus();
  46. atom.commands.dispatch(
  47. document.activeElement,
  48. 'line-ending-selector:convert-to-CRLF'
  49. );
  50. expect(editor.getText()).toBe('Hello\r\nGoodbye\r\nMixed\r\n');
  51. });
  52. });
  53. });
  54. describe('Status bar tile', () => {
  55. describe('when an empty file is opened', () => {
  56. it('uses the default line endings for the platform', () => {
  57. waitsFor(done => {
  58. spyOn(helpers, 'getProcessPlatform').andReturn('win32');
  59. atom.workspace.open('').then(editor => {
  60. const subscription = lineEndingTile.onDidChange(() => {
  61. subscription.dispose();
  62. expect(lineEndingTile.element.textContent).toBe('CRLF');
  63. expect(editor.getBuffer().getPreferredLineEnding()).toBe('\r\n');
  64. expect(getTooltipText(lineEndingTile.element)).toBe(
  65. 'File uses CRLF (Windows) line endings'
  66. );
  67. done();
  68. });
  69. });
  70. });
  71. waitsFor(done => {
  72. helpers.getProcessPlatform.andReturn('darwin');
  73. atom.workspace.open('').then(editor => {
  74. const subscription = lineEndingTile.onDidChange(() => {
  75. subscription.dispose();
  76. expect(lineEndingTile.element.textContent).toBe('LF');
  77. expect(editor.getBuffer().getPreferredLineEnding()).toBe('\n');
  78. expect(getTooltipText(lineEndingTile.element)).toBe(
  79. 'File uses LF (Unix) line endings'
  80. );
  81. done();
  82. });
  83. });
  84. });
  85. });
  86. describe('when the "defaultLineEnding" setting is set to "LF"', () => {
  87. beforeEach(() => {
  88. atom.config.set('line-ending-selector.defaultLineEnding', 'LF');
  89. });
  90. it('uses LF line endings, regardless of the platform', () => {
  91. waitsFor(done => {
  92. spyOn(helpers, 'getProcessPlatform').andReturn('win32');
  93. atom.workspace.open('').then(editor => {
  94. lineEndingTile.onDidChange(() => {
  95. expect(lineEndingTile.element.textContent).toBe('LF');
  96. expect(editor.getBuffer().getPreferredLineEnding()).toBe('\n');
  97. done();
  98. });
  99. });
  100. });
  101. });
  102. });
  103. describe('when the "defaultLineEnding" setting is set to "CRLF"', () => {
  104. beforeEach(() => {
  105. atom.config.set('line-ending-selector.defaultLineEnding', 'CRLF');
  106. });
  107. it('uses CRLF line endings, regardless of the platform', () => {
  108. waitsFor(done => {
  109. atom.workspace.open('').then(editor => {
  110. lineEndingTile.onDidChange(() => {
  111. expect(lineEndingTile.element.textContent).toBe('CRLF');
  112. expect(editor.getBuffer().getPreferredLineEnding()).toBe(
  113. '\r\n'
  114. );
  115. done();
  116. });
  117. });
  118. });
  119. });
  120. });
  121. });
  122. describe('when a file is opened that contains only CRLF line endings', () => {
  123. it('displays "CRLF" as the line ending', () => {
  124. waitsFor(done => {
  125. atom.workspace.open(path.join(__dirname, 'fixtures', 'windows-endings.md')).then(() => {
  126. lineEndingTile.onDidChange(() => {
  127. expect(lineEndingTile.element.textContent).toBe('CRLF');
  128. done();
  129. });
  130. });
  131. });
  132. });
  133. });
  134. describe('when a file is opened that contains only LF line endings', () => {
  135. it('displays "LF" as the line ending', () => {
  136. waitsFor(done => {
  137. atom.workspace.open(path.join(__dirname, 'fixtures', 'unix-endings.md')).then(editor => {
  138. lineEndingTile.onDidChange(() => {
  139. expect(lineEndingTile.element.textContent).toBe('LF');
  140. expect(editor.getBuffer().getPreferredLineEnding()).toBe(null);
  141. done();
  142. });
  143. });
  144. });
  145. });
  146. });
  147. describe('when a file is opened that contains mixed line endings', () => {
  148. it('displays "Mixed" as the line ending', () => {
  149. waitsFor(done => {
  150. atom.workspace.open(path.join(__dirname, 'fixtures', 'mixed-endings.md')).then(() => {
  151. lineEndingTile.onDidChange(() => {
  152. expect(lineEndingTile.element.textContent).toBe('Mixed');
  153. done();
  154. });
  155. });
  156. });
  157. });
  158. });
  159. describe('clicking the tile', () => {
  160. let lineEndingModal, lineEndingSelector;
  161. beforeEach(() => {
  162. jasmine.attachToDOM(atom.views.getView(atom.workspace));
  163. waitsFor(done =>
  164. atom.workspace
  165. .open(path.join(__dirname, 'fixtures', 'unix-endings.md'))
  166. .then(() => lineEndingTile.onDidChange(done))
  167. );
  168. });
  169. describe('when the text editor has focus', () => {
  170. it('opens the line ending selector modal for the text editor', () => {
  171. atom.workspace.getCenter().activate();
  172. const item = atom.workspace.getActivePaneItem();
  173. expect(item.getFileName && item.getFileName()).toBe(
  174. 'unix-endings.md'
  175. );
  176. lineEndingTile.element.dispatchEvent(new MouseEvent('click', {}));
  177. lineEndingModal = atom.workspace.getModalPanels()[0];
  178. lineEndingSelector = lineEndingModal.getItem();
  179. expect(lineEndingModal.isVisible()).toBe(true);
  180. expect(
  181. lineEndingSelector.element.contains(document.activeElement)
  182. ).toBe(true);
  183. let listItems = lineEndingSelector.element.querySelectorAll('li');
  184. expect(listItems[0].textContent).toBe('LF');
  185. expect(listItems[1].textContent).toBe('CRLF');
  186. });
  187. });
  188. describe('when the text editor does not have focus', () => {
  189. it('opens the line ending selector modal for the active text editor', () => {
  190. atom.workspace.getLeftDock().activate();
  191. const item = atom.workspace.getActivePaneItem();
  192. expect(item instanceof TextEditor).toBe(false);
  193. lineEndingTile.element.dispatchEvent(new MouseEvent('click', {}));
  194. lineEndingModal = atom.workspace.getModalPanels()[0];
  195. lineEndingSelector = lineEndingModal.getItem();
  196. expect(lineEndingModal.isVisible()).toBe(true);
  197. expect(
  198. lineEndingSelector.element.contains(document.activeElement)
  199. ).toBe(true);
  200. let listItems = lineEndingSelector.element.querySelectorAll('li');
  201. expect(listItems[0].textContent).toBe('LF');
  202. expect(listItems[1].textContent).toBe('CRLF');
  203. });
  204. });
  205. describe('when selecting a different line ending for the file', () => {
  206. it('changes the line endings in the buffer', () => {
  207. lineEndingTile.element.dispatchEvent(new MouseEvent('click', {}));
  208. lineEndingModal = atom.workspace.getModalPanels()[0];
  209. lineEndingSelector = lineEndingModal.getItem();
  210. const lineEndingChangedPromise = new Promise(resolve => {
  211. lineEndingTile.onDidChange(() => {
  212. expect(lineEndingTile.element.textContent).toBe('CRLF');
  213. const editor = atom.workspace.getActiveTextEditor();
  214. expect(editor.getText()).toBe('Hello\r\nGoodbye\r\nUnix\r\n');
  215. expect(editor.getBuffer().getPreferredLineEnding()).toBe('\r\n');
  216. resolve();
  217. });
  218. });
  219. lineEndingSelector.refs.queryEditor.setText('CR');
  220. lineEndingSelector.confirmSelection();
  221. expect(lineEndingModal.isVisible()).toBe(false);
  222. waitsForPromise(() => lineEndingChangedPromise);
  223. });
  224. });
  225. describe('when modal is exited', () => {
  226. it('leaves the tile selection as-is', () => {
  227. lineEndingTile.element.dispatchEvent(new MouseEvent('click', {}));
  228. lineEndingModal = atom.workspace.getModalPanels()[0];
  229. lineEndingSelector = lineEndingModal.getItem();
  230. lineEndingSelector.cancelSelection();
  231. expect(lineEndingTile.element.textContent).toBe('LF');
  232. });
  233. });
  234. });
  235. describe('closing the last text editor', () => {
  236. it('displays no line ending in the status bar', () => {
  237. waitsForPromise(() => {
  238. return atom.workspace.open(path.join(__dirname, 'fixtures', 'unix-endings.md')).then(() => {
  239. atom.workspace.getActivePane().destroy();
  240. expect(lineEndingTile.element.textContent).toBe('');
  241. });
  242. });
  243. });
  244. });
  245. describe("when the buffer's line endings change", () => {
  246. let editor;
  247. beforeEach(() => {
  248. waitsFor(done => {
  249. atom.workspace.open(path.join(__dirname, 'fixtures', 'unix-endings.md')).then(e => {
  250. editor = e;
  251. lineEndingTile.onDidChange(done);
  252. });
  253. });
  254. });
  255. it('updates the line ending text in the tile', () => {
  256. let tileText = lineEndingTile.element.textContent;
  257. let tileUpdateCount = 0;
  258. Object.defineProperty(lineEndingTile.element, 'textContent', {
  259. get() {
  260. return tileText;
  261. },
  262. set(text) {
  263. tileUpdateCount++;
  264. tileText = text;
  265. }
  266. });
  267. expect(lineEndingTile.element.textContent).toBe('LF');
  268. expect(getTooltipText(lineEndingTile.element)).toBe(
  269. 'File uses LF (Unix) line endings'
  270. );
  271. waitsFor(done => {
  272. editor.setTextInBufferRange([[0, 0], [0, 0]], '... ');
  273. editor.setTextInBufferRange([[0, Infinity], [1, 0]], '\r\n', {
  274. normalizeLineEndings: false
  275. });
  276. lineEndingTile.onDidChange(done);
  277. });
  278. runs(() => {
  279. expect(tileUpdateCount).toBe(1);
  280. expect(lineEndingTile.element.textContent).toBe('Mixed');
  281. expect(getTooltipText(lineEndingTile.element)).toBe(
  282. 'File uses mixed line endings'
  283. );
  284. });
  285. waitsFor(done => {
  286. atom.commands.dispatch(
  287. editor.getElement(),
  288. 'line-ending-selector:convert-to-CRLF'
  289. );
  290. lineEndingTile.onDidChange(done);
  291. });
  292. runs(() => {
  293. expect(tileUpdateCount).toBe(2);
  294. expect(lineEndingTile.element.textContent).toBe('CRLF');
  295. expect(getTooltipText(lineEndingTile.element)).toBe(
  296. 'File uses CRLF (Windows) line endings'
  297. );
  298. });
  299. waitsFor(done => {
  300. atom.commands.dispatch(
  301. editor.getElement(),
  302. 'line-ending-selector:convert-to-LF'
  303. );
  304. lineEndingTile.onDidChange(done);
  305. });
  306. runs(() => {
  307. expect(tileUpdateCount).toBe(3);
  308. expect(lineEndingTile.element.textContent).toBe('LF');
  309. });
  310. runs(() => {
  311. editor.setTextInBufferRange([[0, 0], [0, 0]], '\n');
  312. });
  313. waits(100);
  314. runs(() => {
  315. expect(tileUpdateCount).toBe(3);
  316. });
  317. });
  318. });
  319. });
  320. });
  321. function getTooltipText(element) {
  322. const [tooltip] = atom.tooltips.findTooltips(element);
  323. return tooltip.getTitle();
  324. }