diff --git a/lib/views/changed-files-count-view.js b/lib/views/changed-files-count-view.js
index 7cfdf864dc..bc7e42f7f7 100644
--- a/lib/views/changed-files-count-view.js
+++ b/lib/views/changed-files-count-view.js
@@ -1,17 +1,14 @@
-/** @jsx etch.dom */
-/* eslint react/no-unknown-property: "off" */
+import React from 'react';
-import etch from 'etch';
-
-export default class ChangedFilesCountView {
- constructor(props) {
- this.props = props;
- etch.initialize(this);
+export default class ChangedFilesCountView extends React.Component {
+ static propTypes = {
+ changedFilesCount: React.PropTypes.number,
+ didClick: React.PropTypes.func,
}
- update(props) {
- this.props = {...this.props, ...props};
- return etch.update(this);
+ static defaultProps = {
+ changedFilesCount: 0,
+ didClick: () => {},
}
render() {
@@ -23,7 +20,7 @@ export default class ChangedFilesCountView {
+ onClick={this.props.didClick}>{label}
);
}
}
diff --git a/lib/views/push-pull-menu-view.js b/lib/views/push-pull-menu-view.js
index 22fdfa89fd..4d3fc11d61 100644
--- a/lib/views/push-pull-menu-view.js
+++ b/lib/views/push-pull-menu-view.js
@@ -1,71 +1,90 @@
-/** @jsx etch.dom */
-/* eslint react/no-unknown-property: "off" */
-
-import etch from 'etch';
+import React from 'react';
import {autobind} from 'core-decorators';
import {GitError} from '../git-shell-out-strategy';
-export default class PushPullMenuView {
- constructor(props) {
- this.props = props;
- this.checkRemote();
- etch.initialize(this);
+export default class PushPullMenuView extends React.Component {
+ static propTypes = {
+ inProgress: React.PropTypes.bool,
+ pullDisabled: React.PropTypes.bool,
+ branchName: React.PropTypes.string,
+ remoteName: React.PropTypes.string,
+ aheadCount: React.PropTypes.number,
+ behindCount: React.PropTypes.number,
}
- update(props) {
- this.props = {...this.props, ...props};
- this.checkRemote();
- return etch.update(this);
+ static defaultProps = {
+ inProgress: false,
+ pullDisabled: false,
}
- checkRemote() {
- if (!this.props.remoteName) {
- this.errorMessage = `Note: No remote detected for branch ${this.props.branchName}. ` +
- 'Pushing will set up a remote tracking branch on remote repo "origin"';
- } else {
- this.errorMessage = '';
- }
+ constructor(props, context) {
+ super(props, context);
+
+ this.state = {
+ errorMessage: '',
+ };
}
render() {
+ const errorMessage = this.getErrorMessage();
+
return (
);
}
+ getErrorMessage() {
+ if (this.state.errorMessage !== '') {
+ return this.state.errorMessage;
+ }
+
+ if (!this.props.remoteName) {
+ return `Note: No remote detected for branch ${this.props.branchName}. ` +
+ 'Pushing will set up a remote tracking branch on remote repo "origin"';
+ }
+
+ return '';
+ }
+
async attemptGitOperation(operation, errorTransform = message => message) {
const operationPromise = operation();
try {
- etch.update(this);
- await operationPromise;
+ return await operationPromise;
} catch (error) {
if (!(error instanceof GitError)) { throw error; }
const {message, description} = errorTransform(error.stdErr);
@@ -73,8 +92,8 @@ export default class PushPullMenuView {
message || 'Cannot complete remote interaction',
{description, dismissable: true},
);
+ return null;
}
- return etch.update(this);
}
@autobind
diff --git a/lib/views/push-pull-view.js b/lib/views/push-pull-view.js
index 10fead429c..41f2bf6b8e 100644
--- a/lib/views/push-pull-view.js
+++ b/lib/views/push-pull-view.js
@@ -1,18 +1,19 @@
-/** @jsx etch.dom */
-/* eslint react/no-unknown-property: "off" */
-
-import etch from 'etch';
+import React from 'react';
import cx from 'classnames';
-export default class PushPullView {
- constructor(props) {
- this.props = props;
- etch.initialize(this);
+export default class PushPullView extends React.Component {
+ static propTypes = {
+ pushInProgress: React.PropTypes.bool,
+ fetchInProgress: React.PropTypes.bool,
+ behindCount: React.PropTypes.number,
+ aheadCount: React.PropTypes.number,
}
- update(props) {
- this.props = {...this.props, ...props};
- return etch.update(this);
+ static defaultProps = {
+ pushInProgress: false,
+ fetchInProgress: false,
+ behindCount: 0,
+ aheadCount: 0,
}
render() {
@@ -21,13 +22,13 @@ export default class PushPullView {
const pushClasses = cx('github-PushPull-icon', 'icon', {'icon-arrow-up': !pushing, 'icon-sync': pushing});
const pullClasses = cx('github-PushPull-icon', 'icon', {'icon-arrow-down': !pulling, 'icon-sync': pulling});
return (
-
+
{ this.element = e; }}>
-
+
{this.props.behindCount ? `${this.props.behindCount}` : ''}
-
+
{this.props.aheadCount ? `${this.props.aheadCount}` : ''}
diff --git a/lib/views/tooltip.js b/lib/views/tooltip.js
index 3d91dbc0d3..7445a3e820 100644
--- a/lib/views/tooltip.js
+++ b/lib/views/tooltip.js
@@ -1,53 +1,105 @@
-/** @jsx etch.dom */
-
-import etch from 'etch';
-import {autobind} from 'core-decorators';
-
-export default class Tooltip {
- constructor({active, text, ...otherProps}, children) {
- this.active = active;
- this.text = text;
- this.children = children;
- this.otherProps = otherProps;
- this.handleMouseOut = this.handleMouseOut;
- this.handleMouseOver = this.handleMouseOver;
- etch.initialize(this);
- }
-
- update({active, text, ...otherProps}, children) {
- this.active = active;
- this.text = text;
- this.children = children;
- this.otherProps = otherProps;
- return etch.update(this);
- }
-
- @autobind
- handleMouseOut() {
- if (this.tooltipDisposable) {
- this.tooltipDisposable.dispose();
- this.tooltipDisposable = null;
- }
+import React from 'react';
+
+import Portal from './portal';
+
+export default class Tooltip extends React.Component {
+ static propTypes = {
+ manager: React.PropTypes.object.isRequired,
+ target: React.PropTypes.func.isRequired,
+ title: React.PropTypes.oneOfType([
+ React.PropTypes.string,
+ React.PropTypes.func,
+ ]),
+ html: React.PropTypes.bool,
+ className: React.PropTypes.string,
+ placement: React.PropTypes.oneOfType([
+ React.PropTypes.string,
+ React.PropTypes.func,
+ ]),
+ trigger: React.PropTypes.oneOf(['hover', 'click', 'focus', 'manual']),
+ showDelay: React.PropTypes.number,
+ hideDelay: React.PropTypes.number,
+ keyBindingCommand: React.PropTypes.string,
+ keyBindingTarget: React.PropTypes.element,
+ children: React.PropTypes.element,
+ }
+
+ constructor(props, context) {
+ super(props, context);
+
+ this.disposable = null;
}
- @autobind
- handleMouseOver() {
- if (this.active && !this.tooltipDisposable) {
- const element = this.element;
- this.tooltipDisposable = atom.tooltips.add(element, {title: this.text, trigger: 'manual'});
+ componentWillReceiveProps(nextProps) {
+ const propKeys = [
+ 'tooltips', 'title', 'html', 'className', 'placement', 'trigger', 'showDelay', 'hideDelay',
+ 'keyBindingCommand', 'keyBindingTarget',
+ ];
+
+ if (propKeys.some(key => this.props[key] !== nextProps[key])) {
+ this.disposable && this.disposable.dispose();
+ this.disposable = null;
+
+ this.setupTooltip(nextProps);
}
}
+ componentDidMount() {
+ this.setupTooltip(this.props);
+ }
+
render() {
- return (
-
- {this.children}
-
- );
+ if (this.props.children !== undefined) {
+ return (
+
{ this.portal = c; }}>{this.props.children}
+ );
+ } else {
+ return null;
+ }
}
- destroy() {
- this.tooltipDisposable && this.tooltipDisposable.dispose();
- etch.destroy(this);
+ componentWillUnmount() {
+ this.disposable && this.disposable.dispose();
+ }
+
+ setupTooltip(props) {
+ if (this.disposable) {
+ return;
+ }
+
+ const options = {};
+ ['title', 'html', 'placement', 'trigger', 'keyBindingCommand', 'keyBindingTarget'].forEach(key => {
+ if (props[key] !== undefined) {
+ options[key] = props[key];
+ }
+ });
+ if (props.className !== undefined) {
+ options.class = props.className;
+ }
+ if (props.showDelay !== undefined || props.hideDelay !== undefined) {
+ const delayDefaults = (props.trigger === 'hover' || props.trigger === undefined)
+ && {show: 1000, hide: 100}
+ || {show: 0, hide: 0};
+
+ options.delay = {
+ show: props.showDelay !== undefined ? props.showDelay : delayDefaults.show,
+ hide: props.hideDelay !== undefined ? props.hideDelay : delayDefaults.hide,
+ };
+ }
+ if (props.children !== undefined) {
+ options.item = this.portal;
+ }
+
+ const target = this.getCurrentTarget(props);
+ this.disposable = props.manager.add(target, options);
+ }
+
+ getCurrentTarget(props) {
+ const target = props.target();
+ if (target !== null && target.element !== undefined) {
+ return target.element;
+ } else {
+ return target;
+ }
}
}
diff --git a/test/controllers/git-controller.test.js b/test/controllers/git-controller.test.js
index 57519ed078..bbd2725db8 100644
--- a/test/controllers/git-controller.test.js
+++ b/test/controllers/git-controller.test.js
@@ -262,9 +262,9 @@ describe('GitController', function() {
const wrapper = shallow(app);
assert.isFalse(wrapper.find('Panel').prop('visible'));
- wrapper.find('StatusBarTileController').prop('toggleGitPanel')();
+ wrapper.find('ObserveModel(StatusBarTileController)').prop('toggleGitPanel')();
assert.isTrue(wrapper.find('Panel').prop('visible'));
- wrapper.find('StatusBarTileController').prop('toggleGitPanel')();
+ wrapper.find('ObserveModel(StatusBarTileController)').prop('toggleGitPanel')();
assert.isFalse(wrapper.find('Panel').prop('visible'));
});
});
diff --git a/test/controllers/status-bar-tile-controller.test.js b/test/controllers/status-bar-tile-controller.test.js
index b49b585bd4..fb42481389 100644
--- a/test/controllers/status-bar-tile-controller.test.js
+++ b/test/controllers/status-bar-tile-controller.test.js
@@ -1,51 +1,73 @@
import fs from 'fs';
import path from 'path';
-import etch from 'etch';
+import React from 'react';
import until from 'test-until';
+import {mount} from 'enzyme';
import {cloneRepository, buildRepository, setUpLocalAndRemoteRepositories} from '../helpers';
import StatusBarTileController from '../../lib/controllers/status-bar-tile-controller';
+import BranchView from '../../lib/views/branch-view';
+import PushPullView from '../../lib/views/push-pull-view';
+import ChangedFilesCountView from '../../lib/views/changed-files-count-view';
describe('StatusBarTileController', function() {
- let atomEnvironment, workspace, workspaceElement, commandRegistry, notificationManager;
+ let atomEnvironment;
+ let workspace, workspaceElement, commandRegistry, notificationManager, tooltips;
+ let component;
beforeEach(function() {
atomEnvironment = global.buildAtomEnvironment();
workspace = atomEnvironment.workspace;
commandRegistry = atomEnvironment.commands;
notificationManager = atomEnvironment.notifications;
+ tooltips = atomEnvironment.tooltips;
workspaceElement = atomEnvironment.views.getView(workspace);
+
+ component = (
+
+ );
});
afterEach(function() {
atomEnvironment.destroy();
});
+ function getTooltipNode(wrapper, selector) {
+ const ts = tooltips.findTooltips(wrapper.find(selector).node.element);
+ assert.lengthOf(ts, 1);
+ ts[0].show();
+ return ts[0].getTooltipElement();
+ }
+
describe('branches', function() {
- it('indicates the current branch and toggles visibility of the branch menu when clicked', async function() {
+ it('indicates the current branch', async function() {
const workdirPath = await cloneRepository('three-files');
const repository = await buildRepository(workdirPath);
- const controller = new StatusBarTileController({workspace, repository, commandRegistry});
- await controller.getLastModelDataRefreshPromise();
- await etch.getScheduler().getNextUpdatePromise();
-
- const branchView = controller.refs.branchView;
- assert.equal(branchView.element.textContent, 'master');
+ const wrapper = mount(React.cloneElement(component, {repository}));
+ await wrapper.instance().refreshModelData();
- // FIXME: Remove this guard when 1.13 is on stable.
- if (parseFloat(atom.getVersion() >= 1.13)) {
- assert.isUndefined(document.querySelectorAll('.github-BranchMenuView')[0]);
- branchView.element.click();
- assert.isDefined(document.querySelectorAll('.github-BranchMenuView')[0]);
- branchView.element.click();
- assert.isUndefined(document.querySelectorAll('.github-BranchMenuView')[0]);
- }
+ assert.equal(wrapper.find(BranchView).prop('branchName'), 'master');
});
describe('the branch menu', function() {
+ function selectOption(tip, value) {
+ const selects = Array.from(tip.getElementsByTagName('select'));
+ assert.lengthOf(selects, 1);
+ const select = selects[0];
+ select.value = value;
+
+ const event = new Event('change', {bubbles: true, cancelable: true});
+ select.dispatchEvent(event);
+ }
+
describe('checking out an existing branch', function() {
it('can check out existing branches with no conflicts', async function() {
const workdirPath = await cloneRepository('three-files');
@@ -54,27 +76,28 @@ describe('StatusBarTileController', function() {
// create branch called 'branch'
await repository.git.exec(['branch', 'branch']);
- const controller = new StatusBarTileController({workspace, repository, commandRegistry});
- await controller.getLastModelDataRefreshPromise();
- await etch.getScheduler().getNextUpdatePromise();
+ const wrapper = mount(React.cloneElement(component, {repository}));
+ await wrapper.instance().refreshModelData();
- const branchMenuView = controller.branchMenuView;
- const {list} = branchMenuView.refs;
+ const tip = getTooltipNode(wrapper, BranchView);
- const branches = Array.from(list.options).map(option => option.value);
+ const branches = Array.from(tip.getElementsByTagName('option'), e => e.innerHTML);
assert.deepEqual(branches, ['branch', 'master']);
+
assert.equal(await repository.getCurrentBranch(), 'master');
- assert.equal(list.selectedOptions[0].value, 'master');
+ assert.equal(tip.querySelector('select').value, 'master');
- list.selectedIndex = branches.indexOf('branch');
- list.onchange();
+ selectOption(tip, 'branch');
assert.equal(await repository.getCurrentBranch(), 'branch');
- assert.equal(list.selectedOptions[0].value, 'branch');
+ assert.equal(tip.querySelector('select').value, 'branch');
+ await wrapper.instance().refreshModelData();
+ assert.equal(tip.querySelector('select').value, 'branch');
- list.selectedIndex = branches.indexOf('master');
- list.onchange();
+ selectOption(tip, 'master');
assert.equal(await repository.getCurrentBranch(), 'master');
- assert.equal(list.selectedOptions[0].value, 'master');
+ assert.equal(tip.querySelector('select').value, 'master');
+ await wrapper.instance().refreshModelData();
+ assert.equal(tip.querySelector('select').value, 'master');
});
it('displays an error message if checkout fails', async function() {
@@ -89,25 +112,25 @@ describe('StatusBarTileController', function() {
await repository.checkout('branch');
fs.writeFileSync(path.join(localRepoPath, 'a.txt'), 'a change that conflicts');
- const controller = new StatusBarTileController({workspace, repository, commandRegistry, notificationManager});
- await controller.getLastModelDataRefreshPromise();
- await etch.getScheduler().getNextUpdatePromise();
+ const wrapper = mount(React.cloneElement(component, {repository}));
+ await wrapper.instance().refreshModelData();
- const branchMenuView = controller.branchMenuView;
- const {list} = branchMenuView.refs;
+ const tip = getTooltipNode(wrapper, BranchView);
- const branches = Array.from(list.options).map(option => option.value);
assert.equal(await repository.getCurrentBranch(), 'branch');
- assert.equal(list.selectedOptions[0].value, 'branch');
+ assert.equal(tip.querySelector('select').value, 'branch');
sinon.stub(notificationManager, 'addError');
- list.selectedIndex = branches.indexOf('master');
- list.onchange();
- await etch.getScheduler().getNextUpdatePromise();
- assert.equal(await repository.getCurrentBranch(), 'branch');
- await assert.async.equal(list.selectedOptions[0].value, 'branch');
- await assert.async.isTrue(notificationManager.addError.called);
+ selectOption(tip, 'master');
+ // Optimistic render
+ assert.equal(tip.querySelector('select').value, 'master');
+ await until(async () => {
+ await wrapper.instance().refreshModelData();
+ return tip.querySelector('select').value === 'branch';
+ });
+
+ assert.isTrue(notificationManager.addError.called);
const notificationArgs = notificationManager.addError.args[0];
assert.equal(notificationArgs[0], 'Checkout aborted');
assert.match(notificationArgs[1].description, /Local changes to the following would be overwritten/);
@@ -119,110 +142,108 @@ describe('StatusBarTileController', function() {
const workdirPath = await cloneRepository('three-files');
const repository = await buildRepository(workdirPath);
- const controller = new StatusBarTileController({workspace, repository, commandRegistry});
- await controller.getLastModelDataRefreshPromise();
- await etch.getScheduler().getNextUpdatePromise();
+ const wrapper = mount(React.cloneElement(component, {repository}));
+ await wrapper.instance().refreshModelData();
- const branchMenuView = controller.branchMenuView;
- const {list, newBranchButton} = branchMenuView.refs;
+ const tip = getTooltipNode(wrapper, BranchView);
- const branches = Array.from(list.options).map(option => option.value);
+ const branches = Array.from(tip.querySelectorAll('option'), option => option.value);
assert.deepEqual(branches, ['master']);
assert.equal(await repository.getCurrentBranch(), 'master');
- assert.equal(list.selectedOptions[0].value, 'master');
+ assert.equal(tip.querySelector('select').value, 'master');
+
+ tip.querySelector('button').click();
- assert.isDefined(branchMenuView.refs.list);
- assert.isUndefined(branchMenuView.refs.editor);
- newBranchButton.click();
- await etch.getScheduler().getNextUpdatePromise();
- assert.isUndefined(branchMenuView.refs.list);
- assert.isDefined(branchMenuView.refs.editor);
+ assert.lengthOf(tip.querySelectorAll('select'), 0);
+ assert.lengthOf(tip.querySelectorAll('.github-BranchMenuView-editor'), 1);
- branchMenuView.refs.editor.setText('new-branch');
- await newBranchButton.onclick();
- repository.refresh();
- await controller.getLastModelDataRefreshPromise();
- await etch.getScheduler().getNextUpdatePromise();
+ tip.querySelector('atom-text-editor').getModel().setText('new-branch');
+ tip.querySelector('button').click();
- assert.isUndefined(branchMenuView.refs.editor);
- assert.isDefined(branchMenuView.refs.list);
+ await until(async () => {
+ await wrapper.instance().refreshModelData();
+ return tip.querySelectorAll('select').length === 1;
+ });
+ assert.equal(tip.querySelector('select').value, 'new-branch');
assert.equal(await repository.getCurrentBranch(), 'new-branch');
- assert.equal(branchMenuView.refs.list.selectedOptions[0].value, 'new-branch');
+
+ assert.lengthOf(tip.querySelectorAll('.github-BranchMenuView-editor'), 0);
+ assert.lengthOf(tip.querySelectorAll('select'), 1);
});
it('displays an error message if branch already exists', async function() {
const workdirPath = await cloneRepository('three-files');
const repository = await buildRepository(workdirPath);
-
await repository.git.exec(['checkout', '-b', 'branch']);
- const controller = new StatusBarTileController({workspace, repository, commandRegistry, notificationManager});
- await controller.getLastModelDataRefreshPromise();
- await etch.getScheduler().getNextUpdatePromise();
-
- const branchMenuView = controller.branchMenuView;
- const {list, newBranchButton} = branchMenuView.refs;
+ const wrapper = mount(React.cloneElement(component, {repository}));
+ await wrapper.instance().refreshModelData();
+ const tip = getTooltipNode(wrapper, BranchView);
sinon.stub(notificationManager, 'addError');
- const branches = Array.from(branchMenuView.refs.list.options).map(option => option.value);
+ const branches = Array.from(tip.getElementsByTagName('option'), option => option.value);
assert.deepEqual(branches, ['branch', 'master']);
assert.equal(await repository.getCurrentBranch(), 'branch');
- assert.equal(list.selectedOptions[0].value, 'branch');
+ assert.equal(tip.querySelector('select').value, 'branch');
- await newBranchButton.onclick();
+ tip.querySelector('button').click();
+ tip.querySelector('atom-text-editor').getModel().setText('master');
+ tip.querySelector('button').click();
- branchMenuView.refs.editor.setText('master');
- await newBranchButton.onclick();
await assert.async.isTrue(notificationManager.addError.called);
const notificationArgs = notificationManager.addError.args[0];
assert.equal(notificationArgs[0], 'Cannot create branch');
assert.match(notificationArgs[1].description, /already exists/);
assert.equal(await repository.getCurrentBranch(), 'branch');
- assert.equal(branchMenuView.refs.list.selectedOptions[0].value, 'branch');
+ assert.equal(tip.querySelector('select').value, 'branch');
});
});
});
});
describe('pushing and pulling', function() {
- it('indicates the ahead and behind counts and toggles visibility of the push pull menu when clicked', async function() {
+ it('shows and hides the PushPullView', async function() {
const {localRepoPath} = await setUpLocalAndRemoteRepositories();
const repository = await buildRepository(localRepoPath);
- const controller = new StatusBarTileController({workspace, repository, commandRegistry});
- await controller.getLastModelDataRefreshPromise();
- await etch.getScheduler().getNextUpdatePromise();
+ const wrapper = mount(React.cloneElement(component, {repository}));
+ await wrapper.instance().refreshModelData();
- const pushPullView = controller.refs.pushPullView;
- const {aheadCount, behindCount} = pushPullView.refs;
- assert.equal(aheadCount.textContent, '');
- assert.equal(behindCount.textContent, '');
+ assert.lengthOf(document.querySelectorAll('.github-PushPullMenuView'), 0);
+ wrapper.find(PushPullView).node.element.click();
+ assert.lengthOf(document.querySelectorAll('.github-PushPullMenuView'), 1);
+ wrapper.find(PushPullView).node.element.click();
+ assert.lengthOf(document.querySelectorAll('.github-PushPullMenuView'), 0);
+ });
+
+ it('indicates the ahead and behind counts', async function() {
+ const {localRepoPath} = await setUpLocalAndRemoteRepositories();
+ const repository = await buildRepository(localRepoPath);
+
+ const wrapper = mount(React.cloneElement(component, {repository}));
+ await wrapper.instance().refreshModelData();
+
+ const tip = getTooltipNode(wrapper, PushPullView);
+
+ assert.equal(tip.querySelector('.github-PushPullMenuView-pull').textContent.trim(), 'Pull');
+ assert.equal(tip.querySelector('.github-PushPullMenuView-push').textContent.trim(), 'Push');
await repository.git.exec(['reset', '--hard', 'head~2']);
repository.refresh();
- await controller.getLastModelDataRefreshPromise();
- await etch.getScheduler().getNextUpdatePromise();
- assert.equal(aheadCount.textContent, '');
- assert.equal(behindCount.textContent, '2');
+ await wrapper.instance().refreshModelData();
+
+ assert.equal(tip.querySelector('.github-PushPullMenuView-pull').textContent.trim(), 'Pull (2)');
+ assert.equal(tip.querySelector('.github-PushPullMenuView-push').textContent.trim(), 'Push');
await repository.git.commit('new local commit', {allowEmpty: true});
repository.refresh();
- await controller.getLastModelDataRefreshPromise();
- await etch.getScheduler().getNextUpdatePromise();
- assert.equal(aheadCount.textContent, '1');
- assert.equal(behindCount.textContent, '2');
-
- // FIXME: Remove this guard when 1.13 is on stable.
- if (parseFloat(atom.getVersion() >= 1.13)) {
- assert.isUndefined(document.querySelectorAll('.github-PushPullMenuView')[0]);
- pushPullView.element.click();
- assert.isDefined(document.querySelectorAll('.github-PushPullMenuView')[0]);
- pushPullView.element.click();
- assert.isUndefined(document.querySelectorAll('.github-PushPullMenuView')[0]);
- }
+ await wrapper.instance().refreshModelData();
+
+ assert.equal(tip.querySelector('.github-PushPullMenuView-pull').textContent.trim(), 'Pull (2)');
+ assert.equal(tip.querySelector('.github-PushPullMenuView-push').textContent.trim(), 'Push (1)');
});
describe('the push/pull menu', function() {
@@ -231,26 +252,27 @@ describe('StatusBarTileController', function() {
const repository = await buildRepository(localRepoPath);
await repository.git.exec(['checkout', '-b', 'new-branch']);
- const controller = new StatusBarTileController({workspace, repository, commandRegistry});
- await controller.getLastModelDataRefreshPromise();
- await etch.getScheduler().getNextUpdatePromise();
+ const wrapper = mount(React.cloneElement(component, {repository}));
+ await wrapper.instance().refreshModelData();
+
+ const tip = getTooltipNode(wrapper, PushPullView);
- const pushPullMenuView = controller.pushPullMenuView;
- const {pushButton, pullButton, fetchButton, message} = pushPullMenuView.refs;
+ const pullButton = tip.querySelector('button.github-PushPullMenuView-pull');
+ const pushButton = tip.querySelector('button.github-PushPullMenuView-push');
+ const message = tip.querySelector('.github-PushPullMenuView-message');
assert.isTrue(pullButton.disabled);
- assert.isTrue(fetchButton.disabled);
+ assert.isFalse(pushButton.disabled);
assert.match(message.innerHTML, /No remote detected.*Pushing will set up a remote tracking branch/);
- pushButton.dispatchEvent(new MouseEvent('click'));
+ pushButton.click();
await until(async fail => {
try {
repository.refresh();
- await controller.getLastModelDataRefreshPromise();
- await etch.getScheduler().getNextUpdatePromise();
+ await wrapper.instance().refreshModelData();
assert.isFalse(pullButton.disabled);
- assert.isFalse(fetchButton.disabled);
+ assert.isFalse(pushButton.disabled);
assert.equal(message.textContent, '');
return true;
} catch (err) {
@@ -265,30 +287,29 @@ describe('StatusBarTileController', function() {
await repository.git.exec(['reset', '--hard', 'head~2']);
await repository.git.commit('another commit', {allowEmpty: true});
- const controller = new StatusBarTileController({workspace, repository, commandRegistry, notificationManager});
- await controller.getLastModelDataRefreshPromise();
- await etch.getScheduler().getNextUpdatePromise();
+ const wrapper = mount(React.cloneElement(component, {repository}));
+ await wrapper.instance().refreshModelData();
- const pushPullMenuView = controller.pushPullMenuView;
- const {pushButton, pullButton} = pushPullMenuView.refs;
+ const tip = getTooltipNode(wrapper, PushPullView);
+
+ const pullButton = tip.querySelector('button.github-PushPullMenuView-pull');
+ const pushButton = tip.querySelector('button.github-PushPullMenuView-push');
sinon.stub(notificationManager, 'addError');
assert.equal(pushButton.textContent, 'Push (1)');
assert.equal(pullButton.textContent, 'Pull (2)');
- pushButton.dispatchEvent(new MouseEvent('click'));
- await controller.getLastModelDataRefreshPromise();
- await etch.getScheduler().getNextUpdatePromise();
+ pushButton.click();
+ await wrapper.instance().refreshModelData();
await assert.async.isTrue(notificationManager.addError.called);
const notificationArgs = notificationManager.addError.args[0];
assert.equal(notificationArgs[0], 'Push rejected');
assert.match(notificationArgs[1].description, /Try pulling before pushing again/);
- pushButton.dispatchEvent(new MouseEvent('click', {metaKey: true}));
- repository.refresh();
- await controller.getLastModelDataRefreshPromise();
+ pushButton.dispatchEvent(new MouseEvent('click', {metaKey: true, bubbles: true}));
+ await wrapper.instance().refreshModelData();
await assert.async.equal(pushButton.textContent, 'Push ');
await assert.async.equal(pullButton.textContent, 'Pull ');
@@ -300,8 +321,8 @@ describe('StatusBarTileController', function() {
const {localRepoPath} = await setUpLocalAndRemoteRepositories('multiple-commits', {remoteAhead: true});
const repository = await buildRepository(localRepoPath);
- const controller = new StatusBarTileController({workspace, repository, commandRegistry});
- await controller.getLastModelDataRefreshPromise();
+ const wrapper = mount(React.cloneElement(component, {repository}));
+ await wrapper.instance().refreshModelData();
sinon.spy(repository, 'fetch');
@@ -314,8 +335,8 @@ describe('StatusBarTileController', function() {
const {localRepoPath} = await setUpLocalAndRemoteRepositories('multiple-commits', {remoteAhead: true});
const repository = await buildRepository(localRepoPath);
- const controller = new StatusBarTileController({workspace, repository, commandRegistry});
- await controller.getLastModelDataRefreshPromise();
+ const wrapper = mount(React.cloneElement(component, {repository}));
+ await wrapper.instance().refreshModelData();
sinon.spy(repository, 'pull');
@@ -328,8 +349,8 @@ describe('StatusBarTileController', function() {
const {localRepoPath} = await setUpLocalAndRemoteRepositories();
const repository = await buildRepository(localRepoPath);
- const controller = new StatusBarTileController({workspace, repository, commandRegistry});
- await controller.getLastModelDataRefreshPromise();
+ const wrapper = mount(React.cloneElement(component, {repository}));
+ await wrapper.instance().refreshModelData();
sinon.spy(repository, 'push');
@@ -342,8 +363,8 @@ describe('StatusBarTileController', function() {
const {localRepoPath} = await setUpLocalAndRemoteRepositories();
const repository = await buildRepository(localRepoPath);
- const controller = new StatusBarTileController({workspace, repository, commandRegistry});
- await controller.getLastModelDataRefreshPromise();
+ const wrapper = mount(React.cloneElement(component, {repository}));
+ await wrapper.instance().refreshModelData();
sinon.spy(repository, 'push');
@@ -360,25 +381,31 @@ describe('StatusBarTileController', function() {
const repository = await buildRepository(workdirPath);
const toggleGitPanel = sinon.spy();
- const controller = new StatusBarTileController({workspace, repository, toggleGitPanel, commandRegistry});
- await controller.getLastModelDataRefreshPromise();
- await etch.getScheduler().getNextUpdatePromise();
- const changedFilesCountView = controller.refs.changedFilesCountView;
+ const wrapper = mount(React.cloneElement(component, {repository, toggleGitPanel}));
+ await wrapper.instance().refreshModelData();
- assert.equal(changedFilesCountView.element.textContent, '0 files');
+ assert.equal(wrapper.find('.github-ChangedFilesCount').render().text(), '0 files');
fs.writeFileSync(path.join(workdirPath, 'a.txt'), 'a change\n');
fs.unlinkSync(path.join(workdirPath, 'b.txt'));
- repository.refresh();
+
await repository.stageFiles(['a.txt']);
repository.refresh();
- await controller.getLastModelDataRefreshPromise();
- await etch.getScheduler().getNextUpdatePromise();
- assert.equal(changedFilesCountView.element.textContent, '2 files');
+ await assert.async.equal(wrapper.find('.github-ChangedFilesCount').render().text(), '2 files');
+ });
+
+ it('toggles the git panel when clicked', async function() {
+ const workdirPath = await cloneRepository('three-files');
+ const repository = await buildRepository(workdirPath);
+
+ const toggleGitPanel = sinon.spy();
+
+ const wrapper = mount(React.cloneElement(component, {repository, toggleGitPanel}));
+ await wrapper.instance().refreshModelData();
- changedFilesCountView.element.click();
+ wrapper.find(ChangedFilesCountView).simulate('click');
assert(toggleGitPanel.calledOnce);
});
});
diff --git a/test/github-package.test.js b/test/github-package.test.js
index ba18531ba8..ff20f4c19f 100644
--- a/test/github-package.test.js
+++ b/test/github-package.test.js
@@ -9,7 +9,8 @@ import {cloneRepository} from './helpers';
import GithubPackage from '../lib/github-package';
describe('GithubPackage', function() {
- let atomEnv, workspace, project, commandRegistry, notificationManager, config, confirm, githubPackage;
+ let atomEnv, workspace, project, commandRegistry, notificationManager, config, confirm, tooltips;
+ let githubPackage;
beforeEach(function() {
atomEnv = global.buildAtomEnvironment();
@@ -17,9 +18,12 @@ describe('GithubPackage', function() {
project = atomEnv.project;
commandRegistry = atomEnv.commands;
notificationManager = atomEnv.notifications;
+ tooltips = atomEnv.tooltips;
config = atomEnv.config;
confirm = atomEnv.confirm.bind(atomEnv);
- githubPackage = new GithubPackage(workspace, project, commandRegistry, notificationManager, config, confirm);
+ githubPackage = new GithubPackage(
+ workspace, project, commandRegistry, notificationManager, tooltips, config, confirm,
+ );
});
afterEach(async function() {
@@ -327,7 +331,9 @@ describe('GithubPackage', function() {
assert.isDefined(payload.resolutionProgressByPath[workdirMergeConflict]);
assert.isUndefined(payload.resolutionProgressByPath[workdirNoConflict]);
- const githubPackage1 = new GithubPackage(workspace, project, commandRegistry, notificationManager, config, confirm);
+ const githubPackage1 = new GithubPackage(
+ workspace, project, commandRegistry, notificationManager, tooltips, config, confirm,
+ );
await githubPackage1.activate(payload);
await githubPackage1.getInitialModelsPromise();