mirror of
https://github.com/atom/atom.git
synced 2026-01-13 00:47:58 -05:00
The startup script now uses a `Set` to manage `GitDiffView`s held in memory and destroy them when `deactivate` is called. There are now four major subscription blocks. 1. The outer subscriptions held by `activate`. 2. The per-editor subscriptions held within `activate`. 3. The per-editor repository event subscriptions held within each `GitDIffView` instance. 4. The per-editor modification event subscriptions held within each `GitDiffView` are only active when the editor content is bound to a valid git repository. Teardowns of any editor or the module now result in `disposal` of the respective editor's subscriptions or all subscriptions authored within the module. I removed some of `GitDiffView`'s unnecessary methods such as the `start`, `cancleUpdate`, `addDecoration` and `removeDecorations`; The last two methods were combined into the body of `updateDiffs`. `scheduleUpdate` now calls `requestAnimationFrame` instead of `setImmediate` because it's native, standard, and yields to other more important browser processes. I know Atom Core implements setImmediate, but rAF seems to work just as fast if not faster. The memory management of the editor markers and diffs have been joined using a WeakMap. When the diffs are destroyed, so too are the editor markers. Finally, I added the `destroy` method to handle the teardown of subscriptions and other destroyable objects contained within the `GitDiffViews` before object release.
314 lines
10 KiB
JavaScript
314 lines
10 KiB
JavaScript
const path = require('path');
|
|
const fs = require('fs-plus');
|
|
const temp = require('temp').track();
|
|
|
|
describe('GitDiff package', () => {
|
|
let editor, editorElement, projectPath, screenUpdates;
|
|
|
|
beforeEach(() => {
|
|
screenUpdates = 0;
|
|
spyOn(window, 'requestAnimationFrame').andCallFake(fn => {
|
|
fn();
|
|
screenUpdates++;
|
|
});
|
|
spyOn(window, 'cancelAnimationFrame').andCallFake(i => null);
|
|
|
|
projectPath = temp.mkdirSync('git-diff-spec-');
|
|
const otherPath = temp.mkdirSync('some-other-path-');
|
|
|
|
fs.copySync(path.join(__dirname, 'fixtures', 'working-dir'), projectPath);
|
|
fs.moveSync(
|
|
path.join(projectPath, 'git.git'),
|
|
path.join(projectPath, '.git')
|
|
);
|
|
atom.project.setPaths([otherPath, projectPath]);
|
|
|
|
jasmine.attachToDOM(atom.workspace.getElement());
|
|
|
|
waitsForPromise(async () => {
|
|
await atom.workspace.open(path.join(projectPath, 'sample.js'));
|
|
await atom.packages.activatePackage('git-diff');
|
|
});
|
|
|
|
runs(() => {
|
|
editor = atom.workspace.getActiveTextEditor();
|
|
editorElement = atom.views.getView(editor);
|
|
});
|
|
});
|
|
|
|
afterEach(() => {
|
|
temp.cleanup();
|
|
});
|
|
|
|
describe('when the editor has no changes', () => {
|
|
it("doesn't mark the editor", () => {
|
|
waitsFor(() => screenUpdates > 0);
|
|
runs(() => expect(editor.getMarkers().length).toBe(0));
|
|
});
|
|
});
|
|
|
|
describe('when the editor has modified lines', () => {
|
|
it('highlights the modified lines', () => {
|
|
expect(editorElement.querySelectorAll('.git-line-modified').length).toBe(
|
|
0
|
|
);
|
|
editor.insertText('a');
|
|
advanceClock(editor.getBuffer().stoppedChangingDelay);
|
|
|
|
waitsFor(() => editor.getMarkers().length > 0);
|
|
runs(() => {
|
|
expect(
|
|
editorElement.querySelectorAll('.git-line-modified').length
|
|
).toBe(1);
|
|
expect(editorElement.querySelector('.git-line-modified')).toHaveData(
|
|
'buffer-row',
|
|
0
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when the editor has added lines', () => {
|
|
it('highlights the added lines', () => {
|
|
expect(editorElement.querySelectorAll('.git-line-added').length).toBe(0);
|
|
editor.moveToEndOfLine();
|
|
editor.insertNewline();
|
|
editor.insertText('a');
|
|
advanceClock(editor.getBuffer().stoppedChangingDelay);
|
|
waitsFor(() => editor.getMarkers().length > 0);
|
|
runs(() => {
|
|
expect(editorElement.querySelectorAll('.git-line-added').length).toBe(
|
|
1
|
|
);
|
|
expect(editorElement.querySelector('.git-line-added')).toHaveData(
|
|
'buffer-row',
|
|
1
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when the editor has removed lines', () => {
|
|
it('highlights the line preceeding the deleted lines', () => {
|
|
expect(editorElement.querySelectorAll('.git-line-added').length).toBe(0);
|
|
editor.setCursorBufferPosition([5]);
|
|
editor.deleteLine();
|
|
advanceClock(editor.getBuffer().stoppedChangingDelay);
|
|
waitsFor(() => editor.getMarkers().length > 0);
|
|
runs(() => {
|
|
expect(editorElement.querySelectorAll('.git-line-removed').length).toBe(
|
|
1
|
|
);
|
|
expect(editorElement.querySelector('.git-line-removed')).toHaveData(
|
|
'buffer-row',
|
|
4
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when the editor has removed the first line', () => {
|
|
it('highlights the line preceeding the deleted lines', () => {
|
|
expect(editorElement.querySelectorAll('.git-line-added').length).toBe(0);
|
|
editor.setCursorBufferPosition([0, 0]);
|
|
editor.deleteLine();
|
|
advanceClock(editor.getBuffer().stoppedChangingDelay);
|
|
waitsFor(() => editor.getMarkers().length > 0);
|
|
runs(() => {
|
|
expect(
|
|
editorElement.querySelectorAll('.git-previous-line-removed').length
|
|
).toBe(1);
|
|
expect(
|
|
editorElement.querySelector('.git-previous-line-removed')
|
|
).toHaveData('buffer-row', 0);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when a modified line is restored to the HEAD version contents', () => {
|
|
it('removes the diff highlight', () => {
|
|
expect(editorElement.querySelectorAll('.git-line-modified').length).toBe(
|
|
0
|
|
);
|
|
editor.insertText('a');
|
|
advanceClock(editor.getBuffer().stoppedChangingDelay);
|
|
waitsFor(
|
|
() => editorElement.querySelectorAll('.git-line-modified').length > 0
|
|
);
|
|
runs(() => {
|
|
expect(
|
|
editorElement.querySelectorAll('.git-line-modified').length
|
|
).toBe(1);
|
|
editor.backspace();
|
|
advanceClock(editor.getBuffer().stoppedChangingDelay);
|
|
});
|
|
waitsFor(
|
|
() => editorElement.querySelectorAll('.git-line-modified').length < 1
|
|
);
|
|
runs(() => {
|
|
expect(
|
|
editorElement.querySelectorAll('.git-line-modified').length
|
|
).toBe(0);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when a modified file is opened', () => {
|
|
it('highlights the changed lines', () => {
|
|
fs.writeFileSync(
|
|
path.join(projectPath, 'sample.txt'),
|
|
'Some different text.'
|
|
);
|
|
|
|
waitsForPromise(() =>
|
|
atom.workspace.open(path.join(projectPath, 'sample.txt'))
|
|
);
|
|
|
|
runs(() => {
|
|
editor = atom.workspace.getActiveTextEditor();
|
|
editorElement = editor.getElement();
|
|
});
|
|
|
|
waitsFor(() => editor.getMarkers().length > 0);
|
|
|
|
runs(() => {
|
|
expect(
|
|
editorElement.querySelectorAll('.git-line-modified').length
|
|
).toBe(1);
|
|
expect(editorElement.querySelector('.git-line-modified')).toHaveData(
|
|
'buffer-row',
|
|
0
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when the project paths change', () => {
|
|
it("doesn't try to use the destroyed git repository", () => {
|
|
editor.deleteLine();
|
|
atom.project.setPaths([temp.mkdirSync('no-repository')]);
|
|
advanceClock(editor.getBuffer().stoppedChangingDelay);
|
|
waitsFor(() => editor.getMarkers().length === 0);
|
|
runs(() => {
|
|
expect(editor.getMarkers().length).toBe(0);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('move-to-next-diff/move-to-previous-diff events', () => {
|
|
it('moves the cursor to first character of the next/previous diff line', () => {
|
|
editor.insertText('a');
|
|
waitsFor(() => editor.getMarkers().length > 0);
|
|
runs(() => {
|
|
editor.setCursorBufferPosition([5]);
|
|
editor.deleteLine();
|
|
advanceClock(editor.getBuffer().stoppedChangingDelay);
|
|
|
|
editor.setCursorBufferPosition([0]);
|
|
atom.commands.dispatch(editorElement, 'git-diff:move-to-next-diff');
|
|
expect(editor.getCursorBufferPosition()).toEqual([4, 4]);
|
|
|
|
atom.commands.dispatch(editorElement, 'git-diff:move-to-previous-diff');
|
|
expect(editor.getCursorBufferPosition()).toEqual([0, 0]);
|
|
});
|
|
});
|
|
|
|
it('wraps around to the first/last diff in the file', () => {
|
|
editor.insertText('a');
|
|
waitsFor(() => editor.getMarkers().length > 0);
|
|
runs(() => {
|
|
editor.setCursorBufferPosition([5]);
|
|
editor.deleteLine();
|
|
advanceClock(editor.getBuffer().stoppedChangingDelay);
|
|
|
|
editor.setCursorBufferPosition([0]);
|
|
atom.commands.dispatch(editorElement, 'git-diff:move-to-next-diff');
|
|
expect(editor.getCursorBufferPosition().toArray()).toEqual([4, 4]);
|
|
|
|
atom.commands.dispatch(editorElement, 'git-diff:move-to-next-diff');
|
|
expect(editor.getCursorBufferPosition().toArray()).toEqual([0, 0]);
|
|
|
|
atom.commands.dispatch(editorElement, 'git-diff:move-to-previous-diff');
|
|
expect(editor.getCursorBufferPosition().toArray()).toEqual([4, 4]);
|
|
});
|
|
});
|
|
|
|
describe('when the wrapAroundOnMoveToDiff config option is false', () => {
|
|
beforeEach(() =>
|
|
atom.config.set('git-diff.wrapAroundOnMoveToDiff', false)
|
|
);
|
|
|
|
it('does not wraps around to the first/last diff in the file', () => {
|
|
editor.insertText('a');
|
|
editor.setCursorBufferPosition([5]);
|
|
editor.deleteLine();
|
|
advanceClock(editor.getBuffer().stoppedChangingDelay);
|
|
waitsFor(() => editor.getMarkers().length > 0);
|
|
|
|
runs(() => {
|
|
editor.setCursorBufferPosition([0]);
|
|
atom.commands.dispatch(editorElement, 'git-diff:move-to-next-diff');
|
|
expect(editor.getCursorBufferPosition()).toEqual([4, 4]);
|
|
|
|
atom.commands.dispatch(editorElement, 'git-diff:move-to-next-diff');
|
|
expect(editor.getCursorBufferPosition()).toEqual([4, 4]);
|
|
|
|
atom.commands.dispatch(
|
|
editorElement,
|
|
'git-diff:move-to-previous-diff'
|
|
);
|
|
expect(editor.getCursorBufferPosition()).toEqual([0, 0]);
|
|
|
|
atom.commands.dispatch(
|
|
editorElement,
|
|
'git-diff:move-to-previous-diff'
|
|
);
|
|
expect(editor.getCursorBufferPosition()).toEqual([0, 0]);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when the showIconsInEditorGutter config option is true', () => {
|
|
beforeEach(() => {
|
|
atom.config.set('git-diff.showIconsInEditorGutter', true);
|
|
});
|
|
|
|
it('the gutter has a git-diff-icon class', () => {
|
|
waitsFor(() => screenUpdates > 0);
|
|
runs(() => {
|
|
expect(editorElement.querySelector('.gutter')).toHaveClass(
|
|
'git-diff-icon'
|
|
);
|
|
});
|
|
});
|
|
|
|
it('keeps the git-diff-icon class when editor.showLineNumbers is toggled', () => {
|
|
waitsFor(() => screenUpdates > 0);
|
|
|
|
runs(() => {
|
|
atom.config.set('editor.showLineNumbers', false);
|
|
expect(editorElement.querySelector('.gutter')).not.toHaveClass(
|
|
'git-diff-icon'
|
|
);
|
|
|
|
atom.config.set('editor.showLineNumbers', true);
|
|
expect(editorElement.querySelector('.gutter')).toHaveClass(
|
|
'git-diff-icon'
|
|
);
|
|
});
|
|
});
|
|
|
|
it('removes the git-diff-icon class when the showIconsInEditorGutter config option set to false', () => {
|
|
waitsFor(() => screenUpdates > 0);
|
|
|
|
runs(() => {
|
|
atom.config.set('git-diff.showIconsInEditorGutter', false);
|
|
expect(editorElement.querySelector('.gutter')).not.toHaveClass(
|
|
'git-diff-icon'
|
|
);
|
|
});
|
|
});
|
|
});
|
|
});
|