From d426011ce1971e5e5a0d5f15e16e654b072c3d87 Mon Sep 17 00:00:00 2001 From: wadeuk3 Date: Tue, 8 Oct 2019 08:40:08 -0500 Subject: [PATCH 01/64] FR template beginnings --- .../feature-requests/007-remote-management.md | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 docs/feature-requests/007-remote-management.md diff --git a/docs/feature-requests/007-remote-management.md b/docs/feature-requests/007-remote-management.md new file mode 100644 index 0000000000..f322ff3f87 --- /dev/null +++ b/docs/feature-requests/007-remote-management.md @@ -0,0 +1,63 @@ + + +**_Part 1 - Required information_** + +# Remote Management + +## :memo: Summary + +This feature should allow users to manage git remotes within Atom. + +## :checkered_flag: Motivation + +For full support, the minimum features should be as follows. + - Be able to add or delete git remotes. (High Priority) + - Be able to choose a remote to view GitHub PR's from. (High Priority) + +This supports many use cases where people view, commit, pull, or push across multiple forks. It also supports viewing all pull request reviews where their are multiple remotes. Currently, it is only support to see PR's from one remote. + +The expected outcome is to move a lot of remote management from the terminal to Atom. + +## 🤯 Explanation + +Explain the proposal as if it was already implemented in the GitHub package and you were describing it to an Atom user. That generally means: + +- Introducing new named concepts. +- Explaining the feature largely in terms of examples. +- Explaining any changes to existing workflows. +- Design mock-ups or diagrams depicting any new UI that will be introduced. + + +**_Part 2 - Additional information_** + +## :anchor: Drawbacks + +Why should we *not* do this? + +## :thinking: Rationale and alternatives + +- Why is this approach the best in the space of possible approaches? +- What other approaches have been considered and what is the rationale for not choosing them? +- What is the impact of not doing this? + +## :question: Unresolved questions + +- What unresolved questions do you expect to resolve through the Feature Request process before this gets merged? +- What unresolved questions do you expect to resolve through the implementation of this feature before it is released in a new version of the package? + +## :warning: Out of Scope + +- What related issues do you consider out of scope for this Feature Request that could be addressed in the future independently of the solution that comes out of this Feature Request? + +## :construction: Implementation phases + +- Can this functionality be introduced in multiple, distinct, self-contained pull requests? +- A specification for when the feature is considered "done." + +## :white_check_mark: Feature description for Atom release blog post + +- When this feature is shipped, what would we like to say or show in our Atom release blog post (example: http://blog.atom.io/2018/07/31/atom-1-29.html) +- Feel free to drop ideas and gifs here during development +- Once development is complete, write a blurb for the release coordinator to copy/paste into the Atom release blog From d57cf0079b35ddfa4a57475683a9dab2854eeb29 Mon Sep 17 00:00:00 2001 From: wadeuk3 Date: Tue, 8 Oct 2019 09:18:12 -0500 Subject: [PATCH 02/64] rename variable --- lib/controllers/github-tab-controller.js | 6 +++--- lib/views/github-tab-view.js | 4 ++-- test/controllers/github-tab-controller.test.js | 6 +++--- test/fixtures/props/github-tab-props.js | 2 +- test/views/github-tab-view.test.js | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/controllers/github-tab-controller.js b/lib/controllers/github-tab-controller.js index dc850c2e7f..372faae6b9 100644 --- a/lib/controllers/github-tab-controller.js +++ b/lib/controllers/github-tab-controller.js @@ -28,11 +28,11 @@ export default class GitHubTabController extends React.Component { const currentBranch = this.props.branches.getHeadBranch(); let currentRemote = gitHubRemotes.withName(this.props.selectedRemoteName); - let manyRemotesAvailable = false; + let isSelectingRemote = false; if (!currentRemote.isPresent() && gitHubRemotes.size() === 1) { currentRemote = Array.from(gitHubRemotes)[0]; } else if (!currentRemote.isPresent() && gitHubRemotes.size() > 1) { - manyRemotesAvailable = true; + isSelectingRemote = true; } return ( @@ -47,7 +47,7 @@ export default class GitHubTabController extends React.Component { currentBranch={currentBranch} remotes={gitHubRemotes} currentRemote={currentRemote} - manyRemotesAvailable={manyRemotesAvailable} + isSelectingRemote={isSelectingRemote} aheadCount={this.props.aheadCount} pushInProgress={this.props.pushInProgress} isLoading={this.props.isLoading} diff --git a/lib/views/github-tab-view.js b/lib/views/github-tab-view.js index 6ce0dbddd2..36809b249b 100644 --- a/lib/views/github-tab-view.js +++ b/lib/views/github-tab-view.js @@ -21,7 +21,7 @@ export default class GitHubTabView extends React.Component { currentBranch: BranchPropType.isRequired, remotes: RemoteSetPropType.isRequired, currentRemote: RemotePropType.isRequired, - manyRemotesAvailable: PropTypes.bool.isRequired, + isSelectingRemote: PropTypes.bool.isRequired, aheadCount: PropTypes.number, pushInProgress: PropTypes.bool.isRequired, isLoading: PropTypes.bool.isRequired, @@ -66,7 +66,7 @@ export default class GitHubTabView extends React.Component { ); } - if (this.props.manyRemotesAvailable) { + if (this.props.isSelectingRemote) { // No chosen remote, multiple remotes hosted on GitHub instances return ( Date: Tue, 8 Oct 2019 12:42:08 -0500 Subject: [PATCH 03/64] create and pass exit remote function --- lib/views/github-tab-view.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/views/github-tab-view.js b/lib/views/github-tab-view.js index 36809b249b..80d3f9c73a 100644 --- a/lib/views/github-tab-view.js +++ b/lib/views/github-tab-view.js @@ -8,6 +8,7 @@ import { import LoadingView from './loading-view'; import RemoteSelectorView from './remote-selector-view'; import RemoteContainer from '../containers/remote-container'; +import { nullRemote } from '../models/remote'; export default class GitHubTabView extends React.Component { static propTypes = { @@ -62,6 +63,7 @@ export default class GitHubTabView extends React.Component { aheadCount={this.props.aheadCount} onPushBranch={() => this.props.handlePushBranch(this.props.currentBranch, this.props.currentRemote)} + exitCurrentRemote={e => this.props.handleRemoteSelect(e, nullRemote)} /> ); } From 5ae8141be2581fe98482ecc7078418673d47044c Mon Sep 17 00:00:00 2001 From: wadeuk3 Date: Tue, 8 Oct 2019 12:43:06 -0500 Subject: [PATCH 04/64] pass down the exit function --- lib/containers/remote-container.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/containers/remote-container.js b/lib/containers/remote-container.js index 3655edbe65..f790556798 100644 --- a/lib/containers/remote-container.js +++ b/lib/containers/remote-container.js @@ -137,6 +137,7 @@ export default class RemoteContainer extends React.Component { pushInProgress={this.props.pushInProgress} onPushBranch={this.props.onPushBranch} + exitCurrentRemote={this.props.exitCurrentRemote} /> ); } From 04042815cca2a0c6cbadb6751f5a423374c2eb40 Mon Sep 17 00:00:00 2001 From: wadeuk3 Date: Tue, 8 Oct 2019 12:43:49 -0500 Subject: [PATCH 05/64] catch invalid endpoint error when exiting --- lib/containers/comment-decorations-container.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/containers/comment-decorations-container.js b/lib/containers/comment-decorations-container.js index 81fd521b90..c6d464c8da 100644 --- a/lib/containers/comment-decorations-container.js +++ b/lib/containers/comment-decorations-container.js @@ -70,6 +70,7 @@ export default class CommentDecorationsContainer extends React.Component { } const endpoint = repoData.currentRemote.getEndpoint(); + if (!endpoint) return null; const environment = RelayNetworkLayerManager.getEnvironmentForHost(endpoint, token); const query = graphql` query commentDecorationsContainerQuery( From 40b67137c5ac410959fc8e2583285b454502b18b Mon Sep 17 00:00:00 2001 From: wadeuk3 Date: Tue, 8 Oct 2019 17:12:09 -0500 Subject: [PATCH 06/64] Add a temp visual header with working functionality --- lib/controllers/remote-controller.js | 57 +++++++++++++++++++++------- styles/remote-controller.less | 30 +++++++++++++++ 2 files changed, 73 insertions(+), 14 deletions(-) create mode 100644 styles/remote-controller.less diff --git a/lib/controllers/remote-controller.js b/lib/controllers/remote-controller.js index 75097cd3d2..3fb4198598 100644 --- a/lib/controllers/remote-controller.js +++ b/lib/controllers/remote-controller.js @@ -45,23 +45,26 @@ export default class RemoteController extends React.Component { render() { return ( - + {this.renderHeader()} + + onCreatePr={this.onCreatePr} + /> + ); } @@ -88,4 +91,30 @@ export default class RemoteController extends React.Component { }); }); } + + renderHeader() { + const { remote, exitCurrentRemote } = this.props; + return ( +
+ + Current Remote: {remote.getName()} + + ({remote.getOwner() + '/' + remote.getRepo()}) + + + exitCurrentRemote(e)}> + x + +
+ ); + } + + openRemoteInWeb = () => { + const url = this.props.remote.getUrl(); + return new Promise((resolve, reject) => { + shell.openExternal(url, {}, err => { + if (err) { reject(err); } else { resolve(); } + }); + }); + } } diff --git a/styles/remote-controller.less b/styles/remote-controller.less new file mode 100644 index 0000000000..e7c7d43eba --- /dev/null +++ b/styles/remote-controller.less @@ -0,0 +1,30 @@ +@import "variables"; + +.github-RemoteController { + cursor: default; + user-select: none; + + &-header { + font-weight: 600; + color: @text-color-highlight; + padding: @component-padding/2; + display: flex; + align-items: center; + + &::-webkit-details-marker { + color: @text-color-highlight; + } + } + + &-link { + font-weight: 400; + color: @text-color-subtle; + text-decoration: underline; + font-style: italic; + padding: @component-padding/2; + } + + &-link:hover { + + } +} From 15318e10d65aedcefae4ea1b8662ab90af6f2dc1 Mon Sep 17 00:00:00 2001 From: wadeuk3 Date: Tue, 8 Oct 2019 17:33:14 -0500 Subject: [PATCH 07/64] fix lints --- lib/containers/comment-decorations-container.js | 2 +- lib/containers/remote-container.js | 1 + lib/controllers/remote-controller.js | 14 ++++++++------ lib/views/github-tab-view.js | 2 +- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/containers/comment-decorations-container.js b/lib/containers/comment-decorations-container.js index c6d464c8da..a8249292e2 100644 --- a/lib/containers/comment-decorations-container.js +++ b/lib/containers/comment-decorations-container.js @@ -70,7 +70,7 @@ export default class CommentDecorationsContainer extends React.Component { } const endpoint = repoData.currentRemote.getEndpoint(); - if (!endpoint) return null; + if (!endpoint) { return null; } const environment = RelayNetworkLayerManager.getEnvironmentForHost(endpoint, token); const query = graphql` query commentDecorationsContainerQuery( diff --git a/lib/containers/remote-container.js b/lib/containers/remote-container.js index f790556798..82a2cf5095 100644 --- a/lib/containers/remote-container.js +++ b/lib/containers/remote-container.js @@ -32,6 +32,7 @@ export default class RemoteContainer extends React.Component { // Action methods onPushBranch: PropTypes.func.isRequired, + exitCurrentRemote: PropTypes.func.isRequired, } fetchToken = loginModel => { diff --git a/lib/controllers/remote-controller.js b/lib/controllers/remote-controller.js index 3fb4198598..826b73ade3 100644 --- a/lib/controllers/remote-controller.js +++ b/lib/controllers/remote-controller.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {Fragment} from 'react'; import PropTypes from 'prop-types'; import {shell} from 'electron'; @@ -36,6 +36,7 @@ export default class RemoteController extends React.Component { // Actions onPushBranch: PropTypes.func.isRequired, + exitCurrentRemote: PropTypes.func.isRequired, } constructor(props) { @@ -45,7 +46,7 @@ export default class RemoteController extends React.Component { render() { return ( - <> + {this.renderHeader()} - + ); } @@ -93,7 +94,8 @@ export default class RemoteController extends React.Component { } renderHeader() { - const { remote, exitCurrentRemote } = this.props; + const {remote, exitCurrentRemote} = this.props; + /* eslint-disable jsx-a11y/anchor-is-valid */ return (
@@ -102,9 +104,9 @@ export default class RemoteController extends React.Component { ({remote.getOwner() + '/' + remote.getRepo()}) - exitCurrentRemote(e)}> +
); } diff --git a/lib/views/github-tab-view.js b/lib/views/github-tab-view.js index 80d3f9c73a..f5849505d8 100644 --- a/lib/views/github-tab-view.js +++ b/lib/views/github-tab-view.js @@ -8,7 +8,7 @@ import { import LoadingView from './loading-view'; import RemoteSelectorView from './remote-selector-view'; import RemoteContainer from '../containers/remote-container'; -import { nullRemote } from '../models/remote'; +import {nullRemote} from '../models/remote'; export default class GitHubTabView extends React.Component { static propTypes = { From 258d409436df463ef6404c1f228a581d8aadc759 Mon Sep 17 00:00:00 2001 From: simurai Date: Tue, 12 Feb 2019 11:16:30 +0900 Subject: [PATCH 08/64] Add Git + GitHub context headers --- lib/views/github-tab-view.js | 9 +++++++++ lib/views/staging-view.js | 8 ++++++++ styles/project.less | 28 ++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 styles/project.less diff --git a/lib/views/github-tab-view.js b/lib/views/github-tab-view.js index f5849505d8..259f2deefa 100644 --- a/lib/views/github-tab-view.js +++ b/lib/views/github-tab-view.js @@ -34,6 +34,15 @@ export default class GitHubTabView extends React.Component { render() { return (
+
+ + + +
{this.renderRemote()}
diff --git a/lib/views/staging-view.js b/lib/views/staging-view.js index fa73b425fc..65e79e01dc 100644 --- a/lib/views/staging-view.js +++ b/lib/views/staging-view.js @@ -203,6 +203,14 @@ export default class StagingView extends React.Component { className={`github-StagingView ${this.state.selection.getActiveListKey()}-changes-focused`} tabIndex="-1"> {this.renderCommands()} +
+ + +
diff --git a/styles/project.less b/styles/project.less new file mode 100644 index 0000000000..283854da12 --- /dev/null +++ b/styles/project.less @@ -0,0 +1,28 @@ +@import "variables"; + +.github-Project { + display: flex; + align-items: center; + padding: @component-padding/2 @component-padding; + border-bottom: 1px solid @base-border-color; + + &-avatar { + width: 26px; + height: 26px; + border-radius: @component-border-radius; + } + + &-path { + flex: 1; + margin-left: @component-padding; + } + + &-refreshButton { + margin-left: @component-padding; + + &:before { + margin-right: 0; + } + } + +} From ac2bc1649cc51516c758a66aaa6e6d8a03178551 Mon Sep 17 00:00:00 2001 From: wadeuk3 Date: Wed, 9 Oct 2019 21:21:01 -0500 Subject: [PATCH 09/64] undo my ui visuals --- lib/containers/remote-container.js | 2 - lib/controllers/remote-controller.js | 59 +++++++--------------------- lib/views/github-tab-view.js | 1 - 3 files changed, 14 insertions(+), 48 deletions(-) diff --git a/lib/containers/remote-container.js b/lib/containers/remote-container.js index 82a2cf5095..3655edbe65 100644 --- a/lib/containers/remote-container.js +++ b/lib/containers/remote-container.js @@ -32,7 +32,6 @@ export default class RemoteContainer extends React.Component { // Action methods onPushBranch: PropTypes.func.isRequired, - exitCurrentRemote: PropTypes.func.isRequired, } fetchToken = loginModel => { @@ -138,7 +137,6 @@ export default class RemoteContainer extends React.Component { pushInProgress={this.props.pushInProgress} onPushBranch={this.props.onPushBranch} - exitCurrentRemote={this.props.exitCurrentRemote} /> ); } diff --git a/lib/controllers/remote-controller.js b/lib/controllers/remote-controller.js index 826b73ade3..5b8cd5dfdd 100644 --- a/lib/controllers/remote-controller.js +++ b/lib/controllers/remote-controller.js @@ -36,7 +36,6 @@ export default class RemoteController extends React.Component { // Actions onPushBranch: PropTypes.func.isRequired, - exitCurrentRemote: PropTypes.func.isRequired, } constructor(props) { @@ -46,26 +45,23 @@ export default class RemoteController extends React.Component { render() { return ( - - {this.renderHeader()} - - + onCreatePr={this.onCreatePr} + /> ); } @@ -92,31 +88,4 @@ export default class RemoteController extends React.Component { }); }); } - - renderHeader() { - const {remote, exitCurrentRemote} = this.props; - /* eslint-disable jsx-a11y/anchor-is-valid */ - return ( -
- - Current Remote: {remote.getName()} - - ({remote.getOwner() + '/' + remote.getRepo()}) - - - -
- ); - } - - openRemoteInWeb = () => { - const url = this.props.remote.getUrl(); - return new Promise((resolve, reject) => { - shell.openExternal(url, {}, err => { - if (err) { reject(err); } else { resolve(); } - }); - }); - } } diff --git a/lib/views/github-tab-view.js b/lib/views/github-tab-view.js index 259f2deefa..2fc71562fd 100644 --- a/lib/views/github-tab-view.js +++ b/lib/views/github-tab-view.js @@ -72,7 +72,6 @@ export default class GitHubTabView extends React.Component { aheadCount={this.props.aheadCount} onPushBranch={() => this.props.handlePushBranch(this.props.currentBranch, this.props.currentRemote)} - exitCurrentRemote={e => this.props.handleRemoteSelect(e, nullRemote)} /> ); } From 39e6355dc66c076c895dc0fe41ded287dc7f341b Mon Sep 17 00:00:00 2001 From: wadeuk3 Date: Wed, 9 Oct 2019 21:22:02 -0500 Subject: [PATCH 10/64] create remote selector --- lib/views/remote-selector.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 lib/views/remote-selector.js diff --git a/lib/views/remote-selector.js b/lib/views/remote-selector.js new file mode 100644 index 0000000000..50dac8ee85 --- /dev/null +++ b/lib/views/remote-selector.js @@ -0,0 +1,33 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import { + RemoteSetPropType, RemotePropType +} from '../prop-types'; + +export default class RemoteSelector extends React.Component { + static propTypes = { + remotes: RemoteSetPropType.isRequired, + currentRemoteName: PropTypes.string.isRequired, + + handleRemoteSelect: PropTypes.func.isRequired, + } + + render() { + return ( + + ); + } + + renderRemotes = () => { + const remoteOptions = []; + for (let remote of this.props.remotes) { + remoteOptions.push( + + ); + } + return remoteOptions; + }; +}; From 127c968ad51a9dc49b1f120ad2c1d003ffaacee6 Mon Sep 17 00:00:00 2001 From: wadeuk3 Date: Wed, 9 Oct 2019 21:22:18 -0500 Subject: [PATCH 11/64] use remote selector --- lib/views/github-tab-view.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/views/github-tab-view.js b/lib/views/github-tab-view.js index 2fc71562fd..ac09a62052 100644 --- a/lib/views/github-tab-view.js +++ b/lib/views/github-tab-view.js @@ -7,6 +7,7 @@ import { } from '../prop-types'; import LoadingView from './loading-view'; import RemoteSelectorView from './remote-selector-view'; +import RemoteSelector from './remote-selector'; import RemoteContainer from '../containers/remote-container'; import {nullRemote} from '../models/remote'; @@ -32,16 +33,19 @@ export default class GitHubTabView extends React.Component { } render() { + const { currentRemote, remotes, handleRemoteSelect } = this.props; + const currentRemoteName = currentRemote ? currentRemote.name : ' '; return (
- - + handleRemoteSelect(e, remotes.withName(e.target.value))} + /> + null}/>
{this.renderRemote()} From 8b5adca61cc32ede7b56d1e4c7cad3979913fb29 Mon Sep 17 00:00:00 2001 From: wadeuk3 Date: Wed, 9 Oct 2019 22:51:46 -0500 Subject: [PATCH 12/64] finish undo of temp UI --- lib/controllers/remote-controller.js | 2 +- lib/views/github-tab-view.js | 2 +- styles/remote-controller.less | 30 ---------------------------- 3 files changed, 2 insertions(+), 32 deletions(-) delete mode 100644 styles/remote-controller.less diff --git a/lib/controllers/remote-controller.js b/lib/controllers/remote-controller.js index 5b8cd5dfdd..75097cd3d2 100644 --- a/lib/controllers/remote-controller.js +++ b/lib/controllers/remote-controller.js @@ -1,4 +1,4 @@ -import React, {Fragment} from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import {shell} from 'electron'; diff --git a/lib/views/github-tab-view.js b/lib/views/github-tab-view.js index ac09a62052..75f75aa118 100644 --- a/lib/views/github-tab-view.js +++ b/lib/views/github-tab-view.js @@ -9,7 +9,7 @@ import LoadingView from './loading-view'; import RemoteSelectorView from './remote-selector-view'; import RemoteSelector from './remote-selector'; import RemoteContainer from '../containers/remote-container'; -import {nullRemote} from '../models/remote'; + export default class GitHubTabView extends React.Component { static propTypes = { diff --git a/styles/remote-controller.less b/styles/remote-controller.less deleted file mode 100644 index e7c7d43eba..0000000000 --- a/styles/remote-controller.less +++ /dev/null @@ -1,30 +0,0 @@ -@import "variables"; - -.github-RemoteController { - cursor: default; - user-select: none; - - &-header { - font-weight: 600; - color: @text-color-highlight; - padding: @component-padding/2; - display: flex; - align-items: center; - - &::-webkit-details-marker { - color: @text-color-highlight; - } - } - - &-link { - font-weight: 400; - color: @text-color-subtle; - text-decoration: underline; - font-style: italic; - padding: @component-padding/2; - } - - &-link:hover { - - } -} From 108c57752426781dc8504027b360826dcaad789e Mon Sep 17 00:00:00 2001 From: wadeuk3 Date: Wed, 9 Oct 2019 23:02:03 -0500 Subject: [PATCH 13/64] remove proto header from git tab --- lib/views/staging-view.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lib/views/staging-view.js b/lib/views/staging-view.js index 65e79e01dc..fa73b425fc 100644 --- a/lib/views/staging-view.js +++ b/lib/views/staging-view.js @@ -203,14 +203,6 @@ export default class StagingView extends React.Component { className={`github-StagingView ${this.state.selection.getActiveListKey()}-changes-focused`} tabIndex="-1"> {this.renderCommands()} -
- - -
From 6c08bb27b358752cb7e271fd67d696f7cf420ca7 Mon Sep 17 00:00:00 2001 From: wadeuk3 Date: Wed, 9 Oct 2019 23:05:21 -0500 Subject: [PATCH 14/64] fix lint --- lib/views/github-tab-view.js | 8 ++++---- lib/views/remote-selector.js | 17 ++++++++--------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/lib/views/github-tab-view.js b/lib/views/github-tab-view.js index 75f75aa118..7e7baa7880 100644 --- a/lib/views/github-tab-view.js +++ b/lib/views/github-tab-view.js @@ -33,19 +33,19 @@ export default class GitHubTabView extends React.Component { } render() { - const { currentRemote, remotes, handleRemoteSelect } = this.props; + const {currentRemote, remotes, handleRemoteSelect} = this.props; const currentRemoteName = currentRemote ? currentRemote.name : ' '; return (
- + Your Github Avatar handleRemoteSelect(e, remotes.withName(e.target.value))} + handleRemoteSelect={e => handleRemoteSelect(e, remotes.withName(e.target.value))} /> - null}/> + null} />
{this.renderRemote()} diff --git a/lib/views/remote-selector.js b/lib/views/remote-selector.js index 50dac8ee85..809651010e 100644 --- a/lib/views/remote-selector.js +++ b/lib/views/remote-selector.js @@ -1,9 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { - RemoteSetPropType, RemotePropType -} from '../prop-types'; +import {RemoteSetPropType} from '../prop-types'; export default class RemoteSelector extends React.Component { static propTypes = { @@ -15,7 +13,10 @@ export default class RemoteSelector extends React.Component { render() { return ( - {this.renderRemotes()} ); @@ -23,11 +24,9 @@ export default class RemoteSelector extends React.Component { renderRemotes = () => { const remoteOptions = []; - for (let remote of this.props.remotes) { - remoteOptions.push( - - ); + for (const remote of this.props.remotes) { + remoteOptions.push(); } return remoteOptions; }; -}; +} From 5174e887b2f19b158e821c3ea19e55d8d37f6496 Mon Sep 17 00:00:00 2001 From: wadeuk3 Date: Thu, 10 Oct 2019 02:46:53 -0500 Subject: [PATCH 15/64] remove no longer needed error catch --- lib/containers/comment-decorations-container.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/containers/comment-decorations-container.js b/lib/containers/comment-decorations-container.js index a8249292e2..81fd521b90 100644 --- a/lib/containers/comment-decorations-container.js +++ b/lib/containers/comment-decorations-container.js @@ -70,7 +70,6 @@ export default class CommentDecorationsContainer extends React.Component { } const endpoint = repoData.currentRemote.getEndpoint(); - if (!endpoint) { return null; } const environment = RelayNetworkLayerManager.getEnvironmentForHost(endpoint, token); const query = graphql` query commentDecorationsContainerQuery( From 7768c40bde2e5eda3b30544d1c13b42dcf1f18cb Mon Sep 17 00:00:00 2001 From: wadeuk3 Date: Thu, 10 Oct 2019 04:17:13 -0500 Subject: [PATCH 16/64] add beginnings of avatar components and more ecapsulation --- .../githubHeaderContainerQuery.graphql.js | 102 +++++++++++++++++ lib/containers/github-header-container.js | 104 ++++++++++++++++++ lib/views/github-header-view.js | 31 ++++++ lib/views/github-tab-view.js | 39 ++++--- 4 files changed, 260 insertions(+), 16 deletions(-) create mode 100644 lib/containers/__generated__/githubHeaderContainerQuery.graphql.js create mode 100644 lib/containers/github-header-container.js create mode 100644 lib/views/github-header-view.js diff --git a/lib/containers/__generated__/githubHeaderContainerQuery.graphql.js b/lib/containers/__generated__/githubHeaderContainerQuery.graphql.js new file mode 100644 index 0000000000..b9132a0aca --- /dev/null +++ b/lib/containers/__generated__/githubHeaderContainerQuery.graphql.js @@ -0,0 +1,102 @@ +/** + * @flow + * @relayHash 1fc294f177f0be857503309c013873a8 + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest } from 'relay-runtime'; +export type githubHeaderContainerQueryVariables = {||}; +export type githubHeaderContainerQueryResponse = {| + +viewer: {| + +avatarUrl: any + |} +|}; +export type githubHeaderContainerQuery = {| + variables: githubHeaderContainerQueryVariables, + response: githubHeaderContainerQueryResponse, +|}; +*/ + + +/* +query githubHeaderContainerQuery { + viewer { + avatarUrl + id + } +} +*/ + +const node/*: ConcreteRequest*/ = (function(){ +var v0 = { + "kind": "ScalarField", + "alias": null, + "name": "avatarUrl", + "args": null, + "storageKey": null +}; +return { + "kind": "Request", + "fragment": { + "kind": "Fragment", + "name": "githubHeaderContainerQuery", + "type": "Query", + "metadata": null, + "argumentDefinitions": [], + "selections": [ + { + "kind": "LinkedField", + "alias": null, + "name": "viewer", + "storageKey": null, + "args": null, + "concreteType": "User", + "plural": false, + "selections": [ + (v0/*: any*/) + ] + } + ] + }, + "operation": { + "kind": "Operation", + "name": "githubHeaderContainerQuery", + "argumentDefinitions": [], + "selections": [ + { + "kind": "LinkedField", + "alias": null, + "name": "viewer", + "storageKey": null, + "args": null, + "concreteType": "User", + "plural": false, + "selections": [ + (v0/*: any*/), + { + "kind": "ScalarField", + "alias": null, + "name": "id", + "args": null, + "storageKey": null + } + ] + } + ] + }, + "params": { + "operationKind": "query", + "name": "githubHeaderContainerQuery", + "id": null, + "text": "query githubHeaderContainerQuery {\n viewer {\n avatarUrl\n id\n }\n}\n", + "metadata": {} + } +}; +})(); +// prettier-ignore +(node/*: any*/).hash = '8f64904c4645e6e5b69683dd100f6a6e'; +module.exports = node; diff --git a/lib/containers/github-header-container.js b/lib/containers/github-header-container.js new file mode 100644 index 0000000000..cd6c55bd19 --- /dev/null +++ b/lib/containers/github-header-container.js @@ -0,0 +1,104 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import {QueryRenderer, graphql} from 'react-relay'; + +import {incrementCounter} from '../reporter-proxy'; +import {EndpointPropType, RemoteSetPropType} from '../prop-types'; +import RelayNetworkLayerManager from '../relay-network-layer-manager'; +import {UNAUTHENTICATED, INSUFFICIENT} from '../shared/keytar-strategy'; +import ObserveModel from '../views/observe-model'; +import QueryErrorView from '../views/query-error-view'; +import GithubHeaderView from '../views/github-header-view'; + +export default class GithubHeaderContainer extends React.Component { + static propTypes = { + // Connection + loginModel: PropTypes.object.isRequired, + endpoint: EndpointPropType.isRequired, + + avatarUrl: PropTypes.string, + remotes: RemoteSetPropType.isRequired, + currentRemoteName: PropTypes.string.isRequired, + + handleRemoteSelect: PropTypes.func.isRequired, + } + + fetchToken = loginModel => { + return loginModel.getToken(this.props.endpoint.getLoginAccount()); + } + + render() { + return ( + + {this.renderWithToken} + + ); + } + + renderWithToken = token => { + if (token === null) { + return this.renderWithBlankAvatar(); + } + + if (token instanceof Error) { + return ( + + ); + } + + if (token === UNAUTHENTICATED || token === INSUFFICIENT) { + return this.renderWithBlankAvatar(); + } + + const environment = RelayNetworkLayerManager.getEnvironmentForHost(this.props.endpoint, token); + const query = graphql` + query githubHeaderContainerQuery { + viewer { + avatarUrl + } + } + `; + + return ( + this.renderWithResult(result, token)} + /> + ); + } + + renderWithResult({error, props, retry}, token) { + const avatarUrl = !error && props !== null ? props.viewer.avatarUrl : null; + + return ( + + ); + } + + renderWithBlankAvatar = () => this.renderWithResult({props: null}); + + handleLogin = token => { + incrementCounter('github-login'); + this.props.loginModel.setToken(this.props.endpoint.getLoginAccount(), token); + } + + handleLogout = () => { + incrementCounter('github-logout'); + this.props.loginModel.removeToken(this.props.endpoint.getLoginAccount()); + } + + handleTokenRetry = () => this.props.loginModel.didUpdate(); +} diff --git a/lib/views/github-header-view.js b/lib/views/github-header-view.js new file mode 100644 index 0000000000..4772d9b44f --- /dev/null +++ b/lib/views/github-header-view.js @@ -0,0 +1,31 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import {RemoteSetPropType} from '../prop-types'; +import RemoteSelector from './remote-selector'; + +export default class GithubHeaderView extends React.Component { + static propTypes = { + avatarUrl: PropTypes.string, + remotes: RemoteSetPropType.isRequired, + currentRemoteName: PropTypes.string.isRequired, + + handleRemoteSelect: PropTypes.func.isRequired, + } + + render() { + const {avatarUrl} = this.props; + return ( +
+ Avatar + this.props.handleRemoteSelect(e, this.props.remotes.withName(e.target.value))} + /> + null} /> +
+ ); + } +} diff --git a/lib/views/github-tab-view.js b/lib/views/github-tab-view.js index 7e7baa7880..3000301b8c 100644 --- a/lib/views/github-tab-view.js +++ b/lib/views/github-tab-view.js @@ -9,6 +9,7 @@ import LoadingView from './loading-view'; import RemoteSelectorView from './remote-selector-view'; import RemoteSelector from './remote-selector'; import RemoteContainer from '../containers/remote-container'; +import GithubHeaderContainer from '../containers/github-header-container'; export default class GitHubTabView extends React.Component { @@ -33,20 +34,13 @@ export default class GitHubTabView extends React.Component { } render() { - const {currentRemote, remotes, handleRemoteSelect} = this.props; - const currentRemoteName = currentRemote ? currentRemote.name : ' '; + if (this.props.isLoading) { + return ; + } + return (
-
- Your Github Avatar - handleRemoteSelect(e, remotes.withName(e.target.value))} - /> - null} /> -
+ {this.renderHeader()}
{this.renderRemote()}
@@ -54,11 +48,24 @@ export default class GitHubTabView extends React.Component { ); } - renderRemote() { - if (this.props.isLoading) { - return ; - } + renderHeader() { + if (!this.props.currentRemote.isPresent()) { return null; } + + const {currentRemote, remotes, handleRemoteSelect} = this.props; + const currentRemoteName = currentRemote && currentRemote.name ? currentRemote.name : ' '; + return ( + handleRemoteSelect(e, remotes.withName(e.target.value))} + /> + ); + } + + renderRemote() { if (this.props.currentRemote.isPresent()) { // Single, chosen or unambiguous remote return ( From 77c792a0bb7dff92c829c2796d06eb1c9618a19c Mon Sep 17 00:00:00 2001 From: wadeuk3 Date: Thu, 10 Oct 2019 04:57:49 -0500 Subject: [PATCH 17/64] fix lint --- lib/views/github-tab-view.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/views/github-tab-view.js b/lib/views/github-tab-view.js index 3000301b8c..c8d291490f 100644 --- a/lib/views/github-tab-view.js +++ b/lib/views/github-tab-view.js @@ -7,7 +7,6 @@ import { } from '../prop-types'; import LoadingView from './loading-view'; import RemoteSelectorView from './remote-selector-view'; -import RemoteSelector from './remote-selector'; import RemoteContainer from '../containers/remote-container'; import GithubHeaderContainer from '../containers/github-header-container'; From 39a47bbe8e24dde61ee8c53bfdac6ece40cd9987 Mon Sep 17 00:00:00 2001 From: wadeuk3 Date: Thu, 10 Oct 2019 05:13:43 -0500 Subject: [PATCH 18/64] properly handling loading dom --- lib/views/github-tab-view.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/views/github-tab-view.js b/lib/views/github-tab-view.js index c8d291490f..ad60f23892 100644 --- a/lib/views/github-tab-view.js +++ b/lib/views/github-tab-view.js @@ -33,10 +33,6 @@ export default class GitHubTabView extends React.Component { } render() { - if (this.props.isLoading) { - return ; - } - return (
{this.renderHeader()} @@ -48,7 +44,7 @@ export default class GitHubTabView extends React.Component { } renderHeader() { - if (!this.props.currentRemote.isPresent()) { return null; } + if (this.props.isLoading || !this.props.currentRemote.isPresent()) { return null; } const {currentRemote, remotes, handleRemoteSelect} = this.props; const currentRemoteName = currentRemote && currentRemote.name ? currentRemote.name : ' '; @@ -65,6 +61,10 @@ export default class GitHubTabView extends React.Component { } renderRemote() { + if (this.props.isLoading) { + return ; + } + if (this.props.currentRemote.isPresent()) { // Single, chosen or unambiguous remote return ( From 883f3a3be34cc1da0f80bf4f1dcbfb630ca70d18 Mon Sep 17 00:00:00 2001 From: wadeuk3 Date: Thu, 10 Oct 2019 07:28:41 -0500 Subject: [PATCH 19/64] fix typo on remote selector causing it to break --- lib/containers/github-header-container.js | 2 +- lib/views/github-header-view.js | 2 +- lib/views/github-tab-view.js | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/containers/github-header-container.js b/lib/containers/github-header-container.js index cd6c55bd19..0a5baa04a7 100644 --- a/lib/containers/github-header-container.js +++ b/lib/containers/github-header-container.js @@ -83,7 +83,7 @@ export default class GithubHeaderContainer extends React.Component { remotes={this.props.remotes} currentRemoteName={this.props.currentRemoteName} - handleRemoteSelect={this.handleRemoteSelect} + handleRemoteSelect={e => this.props.handleRemoteSelect(e, this.props.remotes.withName(e.target.value))} /> ); } diff --git a/lib/views/github-header-view.js b/lib/views/github-header-view.js index 4772d9b44f..4838cd5d2a 100644 --- a/lib/views/github-header-view.js +++ b/lib/views/github-header-view.js @@ -22,7 +22,7 @@ export default class GithubHeaderView extends React.Component { currentRemoteName={this.props.currentRemoteName} remotes={this.props.remotes} - handleRemoteSelect={e => this.props.handleRemoteSelect(e, this.props.remotes.withName(e.target.value))} + handleRemoteSelect={this.props.handleRemoteSelect} /> null} />
diff --git a/lib/views/github-tab-view.js b/lib/views/github-tab-view.js index ad60f23892..4b074d5bf0 100644 --- a/lib/views/github-tab-view.js +++ b/lib/views/github-tab-view.js @@ -46,16 +46,16 @@ export default class GitHubTabView extends React.Component { renderHeader() { if (this.props.isLoading || !this.props.currentRemote.isPresent()) { return null; } - const {currentRemote, remotes, handleRemoteSelect} = this.props; + const {currentRemote} = this.props; const currentRemoteName = currentRemote && currentRemote.name ? currentRemote.name : ' '; return ( handleRemoteSelect(e, remotes.withName(e.target.value))} + handleRemoteSelect={this.props.handleRemoteSelect} /> ); } From cfee43f3f28e0c54f162b83758fe3d9258d1df39 Mon Sep 17 00:00:00 2001 From: simurai Date: Tue, 12 Feb 2019 11:16:30 +0900 Subject: [PATCH 20/64] Add Git + GitHub context headers --- lib/views/github-tab-view.js | 9 +++++++++ lib/views/staging-view.js | 8 ++++++++ styles/project.less | 28 ++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 styles/project.less diff --git a/lib/views/github-tab-view.js b/lib/views/github-tab-view.js index 6ce0dbddd2..10bf27f172 100644 --- a/lib/views/github-tab-view.js +++ b/lib/views/github-tab-view.js @@ -33,6 +33,15 @@ export default class GitHubTabView extends React.Component { render() { return (
+
+ + + +
{this.renderRemote()}
diff --git a/lib/views/staging-view.js b/lib/views/staging-view.js index fa73b425fc..65e79e01dc 100644 --- a/lib/views/staging-view.js +++ b/lib/views/staging-view.js @@ -203,6 +203,14 @@ export default class StagingView extends React.Component { className={`github-StagingView ${this.state.selection.getActiveListKey()}-changes-focused`} tabIndex="-1"> {this.renderCommands()} +
+ + +
diff --git a/styles/project.less b/styles/project.less new file mode 100644 index 0000000000..283854da12 --- /dev/null +++ b/styles/project.less @@ -0,0 +1,28 @@ +@import "variables"; + +.github-Project { + display: flex; + align-items: center; + padding: @component-padding/2 @component-padding; + border-bottom: 1px solid @base-border-color; + + &-avatar { + width: 26px; + height: 26px; + border-radius: @component-border-radius; + } + + &-path { + flex: 1; + margin-left: @component-padding; + } + + &-refreshButton { + margin-left: @component-padding; + + &:before { + margin-right: 0; + } + } + +} From 8d867b0d25d05f7a2c757e97d3f90ef8f4a92a48 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 15 Oct 2019 15:39:49 -0500 Subject: [PATCH 21/64] add project repo change ability --- lib/controllers/github-tab-controller.js | 2 + lib/controllers/root-controller.js | 3 + lib/github-package.js | 71 ++++++++++++++++-------- 3 files changed, 52 insertions(+), 24 deletions(-) diff --git a/lib/controllers/github-tab-controller.js b/lib/controllers/github-tab-controller.js index dc850c2e7f..971828dbd0 100644 --- a/lib/controllers/github-tab-controller.js +++ b/lib/controllers/github-tab-controller.js @@ -37,6 +37,7 @@ export default class GitHubTabController extends React.Component { return ( ); } diff --git a/lib/controllers/root-controller.js b/lib/controllers/root-controller.js index 53ee82a659..390932dea6 100644 --- a/lib/controllers/root-controller.js +++ b/lib/controllers/root-controller.js @@ -65,6 +65,7 @@ export default class RootController extends React.Component { clone: PropTypes.func.isRequired, // Control + changeProjectWorkingDirectory: PropTypes.func.isRequired, startOpen: PropTypes.bool, startRevealed: PropTypes.bool, } @@ -296,7 +297,9 @@ export default class RootController extends React.Component { ref={itemHolder.setter} repository={this.props.repository} loginModel={this.props.loginModel} + project={this.props.project} workspace={this.props.workspace} + changeProjectWorkingDirectory={this.props.changeProjectWorkingDirectory} /> )} diff --git a/lib/github-package.js b/lib/github-package.js index 3da08abf44..fde94c2a22 100644 --- a/lib/github-package.js +++ b/lib/github-package.js @@ -175,9 +175,21 @@ export default class GithubPackage { return !!event.target.closest('.github-FilePatchListView').querySelector('.is-selected'); }; + const handleActivePaneChange = item => { + const activeRepository = this.getActiveRepository(); + const activeRepositoryPath = activeRepository ? activeRepository.getWorkingDirectoryPath() : null; + this.scheduleActiveContextUpdate({activeRepositoryPath}, {item}); + } + + const handleProjectPathsChange = projectPaths => { + const activeRepository = this.getActiveRepository(); + const activeRepositoryPath = activeRepository ? activeRepository.getWorkingDirectoryPath() : null; + this.scheduleActiveContextUpdate({activeRepositoryPath}, {projectPaths}); + } + this.subscriptions.add( - this.project.onDidChangePaths(this.scheduleActiveContextUpdate), - this.workspace.getCenter().onDidChangeActivePaneItem(this.scheduleActiveContextUpdate), + this.project.onDidChangePaths(handleProjectPathsChange), + this.workspace.getCenter().onDidStopChangingActivePaneItem(handleActivePaneChange), this.styleCalculator.startWatching( 'github-package-styles', ['editor.fontSize', 'editor.fontFamily', 'editor.lineHeight', 'editor.tabLength'], @@ -269,6 +281,10 @@ export default class GithubPackage { })); } + const changeProjectWorkingDirectory = projectWorkingDirectory => { + this.scheduleActiveContextUpdate({activeRepositoryPath: projectWorkingDirectory}); + } + this.renderFn( { this.controller = c; }} @@ -294,6 +310,7 @@ export default class GithubPackage { startOpen={this.startOpen} startRevealed={this.startRevealed} removeFilePatchItem={this.removeFilePatchItem} + changeProjectWorkingDirectory={changeProjectWorkingDirectory} />, this.element, callback, ); } @@ -486,25 +503,30 @@ export default class GithubPackage { return this.switchboard; } - async scheduleActiveContextUpdate(savedState = {}) { + /** + * Extras can be responses from subscribing to events + * For example, in `handleProjectPathsChange` extras contains the projectPaths changes, + * and in `handleActivePaneChange` extras contains the item changes. + */ + async scheduleActiveContextUpdate(savedState = {}, extras = null) { this.switchboard.didScheduleActiveContextUpdate(); - await this.activeContextQueue.push(this.updateActiveContext.bind(this, savedState), {parallel: false}); + await this.activeContextQueue.push(this.updateActiveContext.bind(this, savedState, extras), {parallel: false}); } /** * Derive the git working directory context that should be used for the package's git operations based on the current * state of the Atom workspace. In priority, this prefers: * + * - The preferred git working directory set by the user (This is also the working directory that was active when the + * package was last serialized). * - A git working directory that contains the active pane item in the workspace's center. * - A git working directory corresponding to a single Project. - * - When initially activating the package, the working directory that was active when the package was last - * serialized. * - The current context, unchanged, which may be a `NullWorkdirContext`. * * First updates the pool of resident contexts to match all git working directories that correspond to open * projects and pane items. */ - async getNextContext(savedState) { + async getNextContext(savedState, extras) { const workdirs = new Set( await Promise.all( this.project.getPaths().map(async projectPath => { @@ -530,39 +552,40 @@ export default class GithubPackage { return {itemPath, itemWorkdir}; }; - const active = await fromPaneItem(this.workspace.getCenter().getActivePaneItem()); - this.contextPool.set(workdirs, savedState); + if (savedState.activeRepositoryPath) { + // Preferred git directory (the preferred directory or the last serialized directory). + return this.contextPool.getContext(savedState.activeRepositoryPath); + } + + const active = await fromPaneItem(this.workspace.getCenter().getActivePaneItem()); + if (active.itemPath) { // Prefer an active item return this.contextPool.getContext(active.itemWorkdir || active.itemPath); } - if (this.project.getPaths().length === 1) { + const projectPaths = this.project.getPaths(); + + if (projectPaths.length === 1) { // Single project - const projectPath = this.project.getPaths()[0]; + const projectPath = projectPaths[0]; const activeWorkingDir = await this.workdirCache.find(projectPath); return this.contextPool.getContext(activeWorkingDir || projectPath); } - if (this.project.getPaths().length === 0 && !this.activeContext.getRepository().isUndetermined()) { + if (projectPaths.length === 0 && !this.activeContext.getRepository().isUndetermined()) { // No projects. Revert to the absent context unless we've guessed that more projects are on the way. return WorkdirContext.absent({pipelineManager: this.pipelineManager}); } - // Restore models from saved state. Will return a NullWorkdirContext if this path is not presently - // resident in the pool. - const savedWorkingDir = savedState.activeRepositoryPath; - if (savedWorkingDir) { - return this.contextPool.getContext(savedWorkingDir); - } - return this.activeContext; } - setActiveContext(nextActiveContext) { - if (nextActiveContext !== this.activeContext) { + setActiveContext(nextActiveContext, extras) { + // Always update rendering when paths change. + if (nextActiveContext !== this.activeContext || (extras && extras.projectPaths)) { if (this.activeContext === this.guessedContext) { this.guessedContext.destroy(); this.guessedContext = null; @@ -577,15 +600,15 @@ export default class GithubPackage { } } - async updateActiveContext(savedState = {}) { + async updateActiveContext(savedState = {}, extras = null) { if (this.workspace.isDestroyed()) { return; } this.switchboard.didBeginActiveContextUpdate(); - const nextActiveContext = await this.getNextContext(savedState); - this.setActiveContext(nextActiveContext); + const nextActiveContext = await this.getNextContext(savedState, extras); + this.setActiveContext(nextActiveContext, extras); } async refreshAtomGitRepository(workdir) { From 73b1ff8c53524a9af92ac8ff36fcd07b4c3729ea Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 15 Oct 2019 15:40:22 -0500 Subject: [PATCH 22/64] update project context ui for github tab --- lib/views/github-header-view.js | 27 +++++++++++++++++++++++++++ lib/views/github-tab-view.js | 16 +++++++--------- 2 files changed, 34 insertions(+), 9 deletions(-) create mode 100644 lib/views/github-header-view.js diff --git a/lib/views/github-header-view.js b/lib/views/github-header-view.js new file mode 100644 index 0000000000..ae5209610d --- /dev/null +++ b/lib/views/github-header-view.js @@ -0,0 +1,27 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import path from 'path'; + +export default class GitHubHeaderView extends React.Component { + render() { + return ( +
+ + + +
+ ); + } + + renderProjects = () => { + const projects = []; + for (const projectPath of this.props.projectPaths) { + projects.push(); + } + return projects; + }; +} diff --git a/lib/views/github-tab-view.js b/lib/views/github-tab-view.js index 10bf27f172..e730eaa2b5 100644 --- a/lib/views/github-tab-view.js +++ b/lib/views/github-tab-view.js @@ -7,6 +7,7 @@ import { } from '../prop-types'; import LoadingView from './loading-view'; import RemoteSelectorView from './remote-selector-view'; +import GitHubHeaderView from './github-header-view'; import RemoteContainer from '../containers/remote-container'; export default class GitHubTabView extends React.Component { @@ -28,20 +29,17 @@ export default class GitHubTabView extends React.Component { handlePushBranch: PropTypes.func.isRequired, handleRemoteSelect: PropTypes.func.isRequired, + changeProjectWorkingDirectory: PropTypes.func.isRequired, } render() { return (
-
- - - -
+ this.props.changeProjectWorkingDirectory(e.target.value)} + currentProject={this.props.workingDirectory} + projectPaths={this.props.project.getPaths()} + />
{this.renderRemote()}
From d4ce92222b9f22b6c7848cdf04ec7b58dcb2ae61 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 15 Oct 2019 15:50:47 -0500 Subject: [PATCH 23/64] rename to header-view --- lib/views/github-tab-view.js | 4 ++-- lib/views/{github-header-view.js => header-view.js} | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) rename lib/views/{github-header-view.js => header-view.js} (93%) diff --git a/lib/views/github-tab-view.js b/lib/views/github-tab-view.js index e730eaa2b5..8a78b49965 100644 --- a/lib/views/github-tab-view.js +++ b/lib/views/github-tab-view.js @@ -7,7 +7,7 @@ import { } from '../prop-types'; import LoadingView from './loading-view'; import RemoteSelectorView from './remote-selector-view'; -import GitHubHeaderView from './github-header-view'; +import HeaderView from './header-view'; import RemoteContainer from '../containers/remote-container'; export default class GitHubTabView extends React.Component { @@ -35,7 +35,7 @@ export default class GitHubTabView extends React.Component { render() { return (
- this.props.changeProjectWorkingDirectory(e.target.value)} currentProject={this.props.workingDirectory} projectPaths={this.props.project.getPaths()} diff --git a/lib/views/github-header-view.js b/lib/views/header-view.js similarity index 93% rename from lib/views/github-header-view.js rename to lib/views/header-view.js index ae5209610d..2aa4e019f5 100644 --- a/lib/views/github-header-view.js +++ b/lib/views/header-view.js @@ -2,7 +2,8 @@ import React from 'react'; import PropTypes from 'prop-types'; import path from 'path'; -export default class GitHubHeaderView extends React.Component { +export default class HeaderView extends React.Component { + render() { return (
From 1f3cfc680f09dcee91195c8f1c65d247dcc90ebe Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 15 Oct 2019 15:58:53 -0500 Subject: [PATCH 24/64] add header view to git tab --- lib/controllers/git-tab-controller.js | 1 + lib/controllers/root-controller.js | 1 + lib/views/git-tab-view.js | 6 ++++++ lib/views/staging-view.js | 8 -------- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/controllers/git-tab-controller.js b/lib/controllers/git-tab-controller.js index b75afe868d..5586fba982 100644 --- a/lib/controllers/git-tab-controller.js +++ b/lib/controllers/git-tab-controller.js @@ -119,6 +119,7 @@ export default class GitTabController extends React.Component { openFiles={this.props.openFiles} discardWorkDirChangesForPaths={this.props.discardWorkDirChangesForPaths} undoLastDiscard={this.props.undoLastDiscard} + changeProjectWorkingDirectory={this.props.changeProjectWorkingDirectory} attemptFileStageOperation={this.attemptFileStageOperation} attemptStageAllOperation={this.attemptStageAllOperation} diff --git a/lib/controllers/root-controller.js b/lib/controllers/root-controller.js index 390932dea6..40a8db36ee 100644 --- a/lib/controllers/root-controller.js +++ b/lib/controllers/root-controller.js @@ -285,6 +285,7 @@ export default class RootController extends React.Component { discardWorkDirChangesForPaths={this.discardWorkDirChangesForPaths} undoLastDiscard={this.undoLastDiscard} refreshResolutionProgress={this.refreshResolutionProgress} + changeProjectWorkingDirectory={this.props.changeProjectWorkingDirectory} /> )} diff --git a/lib/views/git-tab-view.js b/lib/views/git-tab-view.js index 53c01408ce..a9da4dd8c9 100644 --- a/lib/views/git-tab-view.js +++ b/lib/views/git-tab-view.js @@ -4,6 +4,7 @@ import cx from 'classnames'; import {CompositeDisposable} from 'atom'; import StagingView from './staging-view'; +import HeaderView from './header-view'; import CommitController from '../controllers/commit-controller'; import RecentCommitsController from '../controllers/recent-commits-controller'; import RefHolder from '../models/ref-holder'; @@ -141,6 +142,11 @@ export default class GitTabView extends React.Component { className={cx('github-Git', {'is-loading': isLoading})} tabIndex="-1" ref={this.props.refRoot.setter}> + this.props.changeProjectWorkingDirectory(e.target.value)} + currentProject={this.props.workingDirectory} + projectPaths={this.props.project.getPaths()} + /> {this.renderCommands()} -
- - -
From 0ceb2ff31b705c083cf08006c3f19067179c746d Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Wed, 16 Oct 2019 00:36:26 -0500 Subject: [PATCH 25/64] remove avatar and refresh button for mvp --- lib/views/header-view.js | 2 -- styles/project.less | 14 -------------- 2 files changed, 16 deletions(-) diff --git a/lib/views/header-view.js b/lib/views/header-view.js index 2aa4e019f5..c621845136 100644 --- a/lib/views/header-view.js +++ b/lib/views/header-view.js @@ -7,13 +7,11 @@ export default class HeaderView extends React.Component { render() { return (
- -
); } diff --git a/styles/project.less b/styles/project.less index 283854da12..8efc162ef3 100644 --- a/styles/project.less +++ b/styles/project.less @@ -6,23 +6,9 @@ padding: @component-padding/2 @component-padding; border-bottom: 1px solid @base-border-color; - &-avatar { - width: 26px; - height: 26px; - border-radius: @component-border-radius; - } - &-path { flex: 1; margin-left: @component-padding; } - &-refreshButton { - margin-left: @component-padding; - - &:before { - margin-right: 0; - } - } - } From 1d8cef81076b076ac2d469d1b52367d088b4b172 Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Wed, 16 Oct 2019 00:48:47 -0500 Subject: [PATCH 26/64] fix lint --- lib/controllers/git-tab-controller.js | 1 + lib/controllers/github-tab-controller.js | 3 +++ lib/github-package.js | 6 +++--- lib/views/git-tab-view.js | 3 ++- lib/views/github-tab-view.js | 1 + lib/views/header-view.js | 10 ++++++++-- 6 files changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/controllers/git-tab-controller.js b/lib/controllers/git-tab-controller.js index 5586fba982..55c0bcf65e 100644 --- a/lib/controllers/git-tab-controller.js +++ b/lib/controllers/git-tab-controller.js @@ -51,6 +51,7 @@ export default class GitTabController extends React.Component { openFiles: PropTypes.func.isRequired, openInitializeDialog: PropTypes.func.isRequired, controllerRef: RefHolderPropType, + changeProjectWorkingDirectory: PropTypes.func.isRequired, }; constructor(props, context) { diff --git a/lib/controllers/github-tab-controller.js b/lib/controllers/github-tab-controller.js index 971828dbd0..74135427ee 100644 --- a/lib/controllers/github-tab-controller.js +++ b/lib/controllers/github-tab-controller.js @@ -8,6 +8,7 @@ import GitHubTabView from '../views/github-tab-view'; export default class GitHubTabController extends React.Component { static propTypes = { + project: PropTypes.object.isRequired, workspace: PropTypes.object.isRequired, repository: PropTypes.object.isRequired, remoteOperationObserver: OperationStateObserverPropType.isRequired, @@ -21,6 +22,8 @@ export default class GitHubTabController extends React.Component { aheadCount: PropTypes.number, pushInProgress: PropTypes.bool.isRequired, isLoading: PropTypes.bool.isRequired, + + changeProjectWorkingDirectory: PropTypes.func.isRequired, } render() { diff --git a/lib/github-package.js b/lib/github-package.js index fde94c2a22..4d3c43ad92 100644 --- a/lib/github-package.js +++ b/lib/github-package.js @@ -179,13 +179,13 @@ export default class GithubPackage { const activeRepository = this.getActiveRepository(); const activeRepositoryPath = activeRepository ? activeRepository.getWorkingDirectoryPath() : null; this.scheduleActiveContextUpdate({activeRepositoryPath}, {item}); - } + }; const handleProjectPathsChange = projectPaths => { const activeRepository = this.getActiveRepository(); const activeRepositoryPath = activeRepository ? activeRepository.getWorkingDirectoryPath() : null; this.scheduleActiveContextUpdate({activeRepositoryPath}, {projectPaths}); - } + }; this.subscriptions.add( this.project.onDidChangePaths(handleProjectPathsChange), @@ -283,7 +283,7 @@ export default class GithubPackage { const changeProjectWorkingDirectory = projectWorkingDirectory => { this.scheduleActiveContextUpdate({activeRepositoryPath: projectWorkingDirectory}); - } + }; this.renderFn( this.props.changeProjectWorkingDirectory(e.target.value)} - currentProject={this.props.workingDirectory} + currentProject={this.props.workingDirectoryPath} projectPaths={this.props.project.getPaths()} />
From 5e3414ab4a3a4c13ad01d201bccec3de6e4c4efd Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Wed, 16 Oct 2019 01:14:19 -0500 Subject: [PATCH 27/64] remove padding --- styles/project.less | 1 - 1 file changed, 1 deletion(-) diff --git a/styles/project.less b/styles/project.less index 8efc162ef3..6dde98f812 100644 --- a/styles/project.less +++ b/styles/project.less @@ -8,7 +8,6 @@ &-path { flex: 1; - margin-left: @component-padding; } } From 691ca1d175cbd5600f8fb18e0c5ee14c8198c476 Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Wed, 16 Oct 2019 01:48:56 -0500 Subject: [PATCH 28/64] fix 'CurrentPullRequestContainer' test warnings --- lib/containers/current-pull-request-container.js | 3 --- lib/controllers/issueish-list-controller.js | 1 - test/containers/current-pull-request-container.test.js | 1 + 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/containers/current-pull-request-container.js b/lib/containers/current-pull-request-container.js index 95cba9103d..705f7ca965 100644 --- a/lib/containers/current-pull-request-container.js +++ b/lib/containers/current-pull-request-container.js @@ -37,8 +37,6 @@ export default class CurrentPullRequestContainer extends React.Component { aheadCount: PropTypes.number, pushInProgress: PropTypes.bool.isRequired, - workspace: PropTypes.object.isRequired, - // Actions onOpenIssueish: PropTypes.func.isRequired, onOpenReviews: PropTypes.func.isRequired, @@ -164,7 +162,6 @@ export default class CurrentPullRequestContainer extends React.Component { total={associatedPullRequests.totalCount} results={associatedPullRequests.nodes} isLoading={false} - workspace={this.props.workspace} endpoint={this.props.endpoint} resultFilter={issueish => issueish.getHeadRepositoryID() === this.props.repository.id} {...this.controllerProps()} diff --git a/lib/controllers/issueish-list-controller.js b/lib/controllers/issueish-list-controller.js index 8c2f405c2c..94635a6cc6 100644 --- a/lib/controllers/issueish-list-controller.js +++ b/lib/controllers/issueish-list-controller.js @@ -56,7 +56,6 @@ export class BareIssueishListController extends React.Component { onOpenMore: PropTypes.func, emptyComponent: PropTypes.func, - workspace: PropTypes.object, endpoint: EndpointPropType, needReviewsButton: PropTypes.bool, }; diff --git a/test/containers/current-pull-request-container.test.js b/test/containers/current-pull-request-container.test.js index 7d37e017c2..8370c82e44 100644 --- a/test/containers/current-pull-request-container.test.js +++ b/test/containers/current-pull-request-container.test.js @@ -45,6 +45,7 @@ describe('CurrentPullRequestContainer', function() { pushInProgress={false} onOpenIssueish={() => {}} + onOpenReviews={() => {}} onCreatePr={() => {}} {...overrideProps} From 0dc2505cf19c8ad878998201cee6ceb88ca59e9b Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Wed, 16 Oct 2019 01:57:38 -0500 Subject: [PATCH 29/64] fix 'GitTabContainer' test warnings --- lib/views/header-view.js | 2 +- test/fixtures/props/git-tab-props.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/views/header-view.js b/lib/views/header-view.js index 7bb6be9286..b6347c8995 100644 --- a/lib/views/header-view.js +++ b/lib/views/header-view.js @@ -14,7 +14,7 @@ export default class HeaderView extends React.Component { return (
diff --git a/test/fixtures/props/git-tab-props.js b/test/fixtures/props/git-tab-props.js index fd3dfa2e65..dc26fe4732 100644 --- a/test/fixtures/props/git-tab-props.js +++ b/test/fixtures/props/git-tab-props.js @@ -25,6 +25,7 @@ export function gitTabItemProps(atomEnv, repository, overrides = {}) { discardWorkDirChangesForPaths: noop, openFiles: noop, openInitializeDialog: noop, + changeProjectWorkingDirectory: noop, ...overrides }; } From 61a39e3850c4cae5508b6e7e0833223abfdeffbb Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Wed, 16 Oct 2019 03:28:58 -0500 Subject: [PATCH 30/64] fix 'GitHubTabContainer' test warnings --- test/fixtures/props/github-tab-props.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/fixtures/props/github-tab-props.js b/test/fixtures/props/github-tab-props.js index d5ae646f10..e813ab2cc2 100644 --- a/test/fixtures/props/github-tab-props.js +++ b/test/fixtures/props/github-tab-props.js @@ -12,6 +12,10 @@ export function gitHubTabItemProps(atomEnv, repository, overrides = {}) { workspace: atomEnv.workspace, repository, loginModel: new GithubLoginModel(InMemoryStrategy), + project: { + getPaths: () => [], + }, + changeProjectWorkingDirectory: () => {}, ...overrides, }; } From c22dc33c08a5458f23478cbd8d40608fc5672ba0 Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Wed, 16 Oct 2019 06:59:35 -0500 Subject: [PATCH 31/64] create generic Error Boundary --- lib/error-boundary.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 lib/error-boundary.js diff --git a/lib/error-boundary.js b/lib/error-boundary.js new file mode 100644 index 0000000000..16f320a934 --- /dev/null +++ b/lib/error-boundary.js @@ -0,0 +1,31 @@ +import React from 'react'; + +export default class ErrorBoundary extends React.Component { + constructor(props) { + super(props); + this.state = { hasError: false, error: null, errorInfo: null }; + } + + static getDerivedStateFromError(error) { + // Update state so the next render will show the fallback UI. + return { hasError: true }; + } + + componentDidCatch(error, errorInfo) { + this.setState({ + error: error, + errorInfo: errorInfo + }); + // You can also log the error to an error reporting service + // console.log({error, errorInfo}); + } + + render() { + if (this.state.hasError) { + // You can render any custom fallback UI + return null; + } + + return this.props.children; + } +} From 59db453b5aaa75fb73f1ddce086ecd8299eb09cb Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Wed, 16 Oct 2019 07:04:44 -0500 Subject: [PATCH 32/64] fix 'Decoration' test warnings --- test/atom/decoration.test.js | 56 +++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/test/atom/decoration.test.js b/test/atom/decoration.test.js index b31b040b5e..bd9a663b23 100644 --- a/test/atom/decoration.test.js +++ b/test/atom/decoration.test.js @@ -7,6 +7,7 @@ import Decoration from '../../lib/atom/decoration'; import AtomTextEditor from '../../lib/atom/atom-text-editor'; import Marker from '../../lib/atom/marker'; import MarkerLayer from '../../lib/atom/marker-layer'; +import ErrorBoundary from '../../lib/error-boundary'; describe('Decoration', function() { let atomEnv, workspace, editor, marker; @@ -128,15 +129,48 @@ describe('Decoration', function() { assert.isNull(editor.gutterWithName(gutterName)); }); - it('throws an error if `gutterName` prop is not supplied for gutter decorations', function() { - const app = ( - -
- This is a subtree -
-
- ); - assert.throws(() => mount(app), 'You are trying to decorate a gutter but did not supply gutterName prop.'); + context('throws an error', function() { + let errors; + + // This consumes the errors rather than printing them to console. + const onError = function(e) { + errors.push(e.error); + e.preventDefault(); + } + + beforeEach(function() { + errors = []; + // register error consumer + window.addEventListener('error', onError); + }); + + afterEach(function() { + errors = []; + // deregister error consumer (important) + window.removeEventListener('error', onError); + }); + + specify('if `gutterName` prop is not supplied for gutter decorations', function() { + const app = ( + + +
+ This is a subtree +
+
+
+ ); + mount(app); + if (errors.length === 1) { + assert(errors[0], 'You are trying to decorate a gutter but did not supply gutterName prop.'); + } else { + window.removeEventListener('error', onError); + for (const error of errors) { + console.error(error); + } + assert.fail(); + } + }); }); }); @@ -254,7 +288,7 @@ describe('Decoration', function() { it('decorates a parent Marker on a prop-provided TextEditor', function() { mount( - + , ); @@ -266,7 +300,7 @@ describe('Decoration', function() { let layerID = null; mount( { layerID = id; }}> - + , ); From 1323ea4fdb0a862c681c131357cb9a07adce4a83 Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Wed, 16 Oct 2019 07:36:37 -0500 Subject: [PATCH 33/64] fix 'Marker' test warnings --- lib/atom/marker.js | 2 +- test/atom/marker.test.js | 36 ++++++++++++++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/lib/atom/marker.js b/lib/atom/marker.js index 0fed0f7e01..8f3358142f 100644 --- a/lib/atom/marker.js +++ b/lib/atom/marker.js @@ -25,7 +25,7 @@ export const DecorableContext = React.createContext(); class BareMarker extends React.Component { static propTypes = { ...markerProps, - id: PropTypes.string, + id: PropTypes.number, bufferRange: RangePropType, markableHolder: RefHolderPropType, children: PropTypes.node, diff --git a/test/atom/marker.test.js b/test/atom/marker.test.js index 297ce3333a..cde3f6e9c6 100644 --- a/test/atom/marker.test.js +++ b/test/atom/marker.test.js @@ -6,6 +6,7 @@ import Marker from '../../lib/atom/marker'; import AtomTextEditor from '../../lib/atom/atom-text-editor'; import RefHolder from '../../lib/models/ref-holder'; import MarkerLayer from '../../lib/atom/marker-layer'; +import ErrorBoundary from '../../lib/error-boundary'; describe('Marker', function() { let atomEnv, workspace, editor, marker, markerID; @@ -135,8 +136,39 @@ describe('Marker', function() { assert.strictEqual(instance.markerHolder.get(), external); }); - it('fails on construction if its ID is invalid', function() { - assert.throws(() => mount(), /Invalid marker ID: 67/); + context('fails on construction', function() { + let errors; + + // This consumes the errors rather than printing them to console. + const onError = function(e) { + errors.push(e.error); + e.preventDefault(); + } + + beforeEach(function() { + errors = []; + // register error consumer + window.addEventListener('error', onError); + }); + + afterEach(function() { + errors = []; + // deregister error consumer (important) + window.removeEventListener('error', onError); + }); + + specify('if its ID is invalid', function() { + mount(); + if (errors.length === 1) { + assert(errors[0], 'Error: Invalid marker ID: 67'); + } else { + window.removeEventListener('error', onError); + for (const error of errors) { + console.error(error); + } + assert.fail(); + } + }); }); it('does not destroy its marker on unmount', function() { From f8df70ecbde070e39207fd516a0235ec422446cc Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Wed, 16 Oct 2019 08:02:51 -0500 Subject: [PATCH 34/64] fix 'IssueDetailContainer' test warnings --- test/containers/issueish-detail-container.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/containers/issueish-detail-container.test.js b/test/containers/issueish-detail-container.test.js index 417f65fc70..cc726c58d1 100644 --- a/test/containers/issueish-detail-container.test.js +++ b/test/containers/issueish-detail-container.test.js @@ -54,6 +54,7 @@ describe('IssueishDetailContainer', function() { switchToIssueish: () => {}, onTitleChange: () => {}, destroy: () => {}, + reportRelayError: () => {}, itemType: IssueishDetailItem, refEditor: new RefHolder(), From ecac2bdf51de7032b300781114a01dbb11ea52b0 Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Wed, 16 Oct 2019 08:31:41 -0500 Subject: [PATCH 35/64] fix 'IssueishSearchContainer' test warnings --- test/containers/issueish-search-container.test.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/containers/issueish-search-container.test.js b/test/containers/issueish-search-container.test.js index e9119f1e5d..cf6c5e0bed 100644 --- a/test/containers/issueish-search-container.test.js +++ b/test/containers/issueish-search-container.test.js @@ -26,6 +26,7 @@ describe('IssueishSearchContainer', function() { onOpenIssueish={() => {}} onOpenSearch={() => {}} + onOpenReviews={() => {}} {...overrideProps} /> @@ -87,6 +88,9 @@ describe('IssueishSearchContainer', function() { }); it('passes an empty result list and an error prop to the controller when errored', async function() { + // Consumes the failing Relay Query console warning + const stub = sinon.stub(console, "warn"); + expectRelayQuery({ name: 'issueishSearchContainerQuery', variables: { @@ -103,6 +107,9 @@ describe('IssueishSearchContainer', function() { .build(); }).resolve(); + // Restores console.warn + stub.restore(); + const wrapper = mount(buildApp({})); await assert.async.isTrue( From ad45fe4796d0708e6bb59dd1104c51de1203ff61 Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Wed, 16 Oct 2019 08:45:29 -0500 Subject: [PATCH 36/64] fix 'ReviewsContainer' test warnings --- test/containers/reviews-container.test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/containers/reviews-container.test.js b/test/containers/reviews-container.test.js index 903922ec00..49e43678a6 100644 --- a/test/containers/reviews-container.test.js +++ b/test/containers/reviews-container.test.js @@ -39,7 +39,9 @@ describe('ReviewsContainer', function() { queryData = { repository: { - pullRequest: {}, + pullRequest: { + headRefOid: '0000000000000000000000000000000000000000', + }, }, }; }); From 3e7fd437979651dfaedee44e4a4e882509f61dfa Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Wed, 16 Oct 2019 08:51:02 -0500 Subject: [PATCH 37/64] fix 'ChangedFileController' test warnings --- test/controllers/changed-file-controller.test.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/controllers/changed-file-controller.test.js b/test/controllers/changed-file-controller.test.js index 6247764cdf..ab5b808173 100644 --- a/test/controllers/changed-file-controller.test.js +++ b/test/controllers/changed-file-controller.test.js @@ -27,6 +27,9 @@ describe('ChangedFileController', function() { keymaps: atomEnv.keymaps, tooltips: atomEnv.tooltips, config: atomEnv.config, + multiFilePatch: { + getFilePatches: () => {}, + }, destroy: () => {}, undoLastDiscard: () => {}, From 924807c757cf510798f28b9543db1c31f19b5e22 Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Wed, 16 Oct 2019 09:47:30 -0500 Subject: [PATCH 38/64] fix 'CommentDecorationsController' test warnings --- lib/controllers/editor-comment-decorations-controller.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/controllers/editor-comment-decorations-controller.js b/lib/controllers/editor-comment-decorations-controller.js index 1cfe855a6f..61e548e85c 100644 --- a/lib/controllers/editor-comment-decorations-controller.js +++ b/lib/controllers/editor-comment-decorations-controller.js @@ -20,7 +20,10 @@ export default class EditorCommentDecorationsController extends React.Component workspace: PropTypes.object.isRequired, editor: PropTypes.object.isRequired, threadsForPath: PropTypes.arrayOf(PropTypes.shape({ - rootCommentID: PropTypes.string.isRequired, + rootCommentID: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.number, + ]).isRequired, position: PropTypes.number, threadID: PropTypes.string.isRequired, })).isRequired, From 7a9d05c5836e61acc1617e24311adcebd84af172 Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Wed, 16 Oct 2019 13:02:23 -0500 Subject: [PATCH 39/64] fix 'CommentDecorationsContainer' test warnings --- test/containers/comment-decorations-container.test.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/containers/comment-decorations-container.test.js b/test/containers/comment-decorations-container.test.js index 6ee4eb3213..a00f9bb1e3 100644 --- a/test/containers/comment-decorations-container.test.js +++ b/test/containers/comment-decorations-container.test.js @@ -184,8 +184,10 @@ describe('CommentDecorationsContainer', function() { it('renders nothing if query errors', function() { const tokenWrapper = localRepoWrapper.find(ObserveModel).renderProp('children')('1234'); + const stub = sinon.stub(console, 'warn'); const resultWrapper = tokenWrapper.find(QueryRenderer).renderProp('render')({ error: 'oh noes', props: null, retry: () => {}}); + stub.restore(); assert.isTrue(resultWrapper.isEmptyRender()); }); @@ -254,12 +256,15 @@ describe('CommentDecorationsContainer', function() { const resultWrapper = tokenWrapper.find(QueryRenderer).renderProp('render')({ error: null, props, retry: () => {}, }); + const stub = sinon.stub(console, 'warn'); const reviewsWrapper = resultWrapper.find(AggregatedReviewsContainer).renderProp('children')({ errors: [new Error('ahhhh')], summaries: [], commentThreads: [], }); + stub.restore(); + assert.isTrue(reviewsWrapper.isEmptyRender()); }); @@ -341,9 +346,11 @@ describe('CommentDecorationsContainer', function() { {thread: {id: 'thread0'}, comments: [{id: 'comment0', path: 'a.txt'}, {id: 'comment1', path: 'a.txt'}]}, ], }); + const stub = sinon.stub(console, 'warn'); const patchWrapper = reviewsWrapper.find(PullRequestPatchContainer).renderProp('children')( new Error('oops'), null, ); + stub.restore(); assert.isTrue(patchWrapper.isEmptyRender()); }); From 0162fc9c69eabb4a51dca12a3175bf2b0cbe7813 Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Wed, 16 Oct 2019 17:42:29 -0500 Subject: [PATCH 40/64] correctly consume Relay Query error on 'IssueishSearchContainer' test --- .../issueish-search-container.test.js | 70 +++++++++++-------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/test/containers/issueish-search-container.test.js b/test/containers/issueish-search-container.test.js index cf6c5e0bed..fb117e3f4a 100644 --- a/test/containers/issueish-search-container.test.js +++ b/test/containers/issueish-search-container.test.js @@ -87,37 +87,49 @@ describe('IssueishSearchContainer', function() { await promise; }); - it('passes an empty result list and an error prop to the controller when errored', async function() { - // Consumes the failing Relay Query console warning - const stub = sinon.stub(console, "warn"); - - expectRelayQuery({ - name: 'issueishSearchContainerQuery', - variables: { - query: 'type:pr', - first: 20, - checkSuiteCount: CHECK_SUITE_PAGE_SIZE, - checkSuiteCursor: null, - checkRunCount: CHECK_RUN_PAGE_SIZE, - checkRunCursor: null, - }, - }, op => { - return relayResponseBuilder(op) - .addError('uh oh') - .build(); - }).resolve(); - - // Restores console.warn - stub.restore(); + context('when the query errors', async function() { + let stub; + // Consumes the failing Relay Query console error + before(function () { + stub = sinon.stub(console, "error"); + console.error.withArgs( + "Error encountered in subquery", + sinon.match.defined.and( + sinon.match.hasNested("errors[0].message", sinon.match("uh oh")) + ) + ).callsFake(() => {}); + console.error.callThrough(); + }); - const wrapper = mount(buildApp({})); + it('passes an empty result list and an error prop to the controller', function() { + expectRelayQuery({ + name: 'issueishSearchContainerQuery', + variables: { + query: 'type:pr', + first: 20, + checkSuiteCount: CHECK_SUITE_PAGE_SIZE, + checkSuiteCursor: null, + checkRunCount: CHECK_RUN_PAGE_SIZE, + checkRunCursor: null, + }, + }, op => { + return relayResponseBuilder(op) + .addError('uh oh') + .build(); + }).resolve(); + + const wrapper = mount(buildApp({})); + await assert.async.isTrue( + wrapper.update().find('BareIssueishListController').filterWhere(n => !n.prop('isLoading')).exists(), + ); + const controller = wrapper.find('BareIssueishListController'); + assert.deepEqual(controller.prop('error').errors, [{message: 'uh oh'}]); + assert.lengthOf(controller.prop('results'), 0); + }); - await assert.async.isTrue( - wrapper.update().find('BareIssueishListController').filterWhere(n => !n.prop('isLoading')).exists(), - ); - const controller = wrapper.find('BareIssueishListController'); - assert.deepEqual(controller.prop('error').errors, [{message: 'uh oh'}]); - assert.lengthOf(controller.prop('results'), 0); + after(function() { + stub.restore(); + }); }); it('passes results to the controller', async function() { From 004d5b4b6a7ea8928273babcd906ca61984f9922 Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Wed, 16 Oct 2019 20:37:13 -0500 Subject: [PATCH 41/64] fix async test error --- test/containers/issueish-search-container.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/containers/issueish-search-container.test.js b/test/containers/issueish-search-container.test.js index fb117e3f4a..743226b3d0 100644 --- a/test/containers/issueish-search-container.test.js +++ b/test/containers/issueish-search-container.test.js @@ -87,7 +87,7 @@ describe('IssueishSearchContainer', function() { await promise; }); - context('when the query errors', async function() { + context('when the query errors', function() { let stub; // Consumes the failing Relay Query console error before(function () { @@ -101,7 +101,7 @@ describe('IssueishSearchContainer', function() { console.error.callThrough(); }); - it('passes an empty result list and an error prop to the controller', function() { + it('passes an empty result list and an error prop to the controller', async function() { expectRelayQuery({ name: 'issueishSearchContainerQuery', variables: { From 32f0360c9a7025f21ae733a8ae76dfb79d5a0139 Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Wed, 16 Oct 2019 21:09:55 -0500 Subject: [PATCH 42/64] update test props for github tab view --- test/fixtures/props/github-tab-props.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/fixtures/props/github-tab-props.js b/test/fixtures/props/github-tab-props.js index e813ab2cc2..7df9494dcb 100644 --- a/test/fixtures/props/github-tab-props.js +++ b/test/fixtures/props/github-tab-props.js @@ -43,6 +43,7 @@ export function gitHubTabControllerProps(atomEnv, repository, overrides = {}) { export function gitHubTabViewProps(atomEnv, repository, overrides = {}) { return { + project: atomEnv.project, workspace: atomEnv.workspace, remoteOperationObserver: new OperationStateObserver(repository, PUSH, PULL, FETCH), loginModel: new GithubLoginModel(InMemoryStrategy), @@ -59,6 +60,7 @@ export function gitHubTabViewProps(atomEnv, repository, overrides = {}) { handlePushBranch: () => {}, handleRemoteSelect: () => {}, + changeProjectWorkingDirectory: () => {}, ...overrides, }; From 52ef584933ec48e9320b933ca7d202639f04e7cc Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Thu, 17 Oct 2019 00:16:48 -0500 Subject: [PATCH 43/64] remove active pane context guessing --- lib/github-package.js | 35 +++++++---------------------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/lib/github-package.js b/lib/github-package.js index 4d3c43ad92..7120639f89 100644 --- a/lib/github-package.js +++ b/lib/github-package.js @@ -519,12 +519,11 @@ export default class GithubPackage { * * - The preferred git working directory set by the user (This is also the working directory that was active when the * package was last serialized). - * - A git working directory that contains the active pane item in the workspace's center. - * - A git working directory corresponding to a single Project. + * - A git working directory corresponding to "first" Project, whether or not there is a single project or multiple. * - The current context, unchanged, which may be a `NullWorkdirContext`. * * First updates the pool of resident contexts to match all git working directories that correspond to open - * projects and pane items. + * projects. */ async getNextContext(savedState, extras) { const workdirs = new Set( @@ -536,22 +535,7 @@ export default class GithubPackage { ), ); - const fromPaneItem = async maybeItem => { - const itemPath = pathForPaneItem(maybeItem); - - if (!itemPath) { - return {}; - } - - const itemWorkdir = await this.workdirCache.find(itemPath); - - if (itemWorkdir && !this.project.contains(itemPath)) { - workdirs.add(itemWorkdir); - } - - return {itemPath, itemWorkdir}; - }; - + // Update pool with the open projects this.contextPool.set(workdirs, savedState); if (savedState.activeRepositoryPath) { @@ -559,17 +543,10 @@ export default class GithubPackage { return this.contextPool.getContext(savedState.activeRepositoryPath); } - const active = await fromPaneItem(this.workspace.getCenter().getActivePaneItem()); - - if (active.itemPath) { - // Prefer an active item - return this.contextPool.getContext(active.itemWorkdir || active.itemPath); - } - const projectPaths = this.project.getPaths(); - if (projectPaths.length === 1) { - // Single project + if (projectPaths.length >= 1) { + // Single or multiple projects (just choose the first, the user can select after) const projectPath = projectPaths[0]; const activeWorkingDir = await this.workdirCache.find(projectPath); return this.contextPool.getContext(activeWorkingDir || projectPath); @@ -580,6 +557,8 @@ export default class GithubPackage { return WorkdirContext.absent({pipelineManager: this.pipelineManager}); } + // It is only possible to reach here if there there was no preferred directory, there are no project paths and the + // the active context's repository is undetermined. return this.activeContext; } From 9cbd1269efdadb09a6c90de0c91fca3949630180 Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Thu, 17 Oct 2019 01:01:12 -0500 Subject: [PATCH 44/64] LINT MEH BABHEH :baby: --- .eslintrc | 2 ++ lib/error-boundary.js | 13 +++++++++---- lib/github-package.js | 1 + test/atom/decoration.test.js | 3 ++- test/atom/marker.test.js | 3 ++- test/containers/issueish-search-container.test.js | 12 ++++++------ 6 files changed, 22 insertions(+), 12 deletions(-) diff --git a/.eslintrc b/.eslintrc index 0c9c6128d0..cacbbfa162 100644 --- a/.eslintrc +++ b/.eslintrc @@ -36,8 +36,10 @@ "describe": true, "fdescribe": true, "xdescribe": true, + "context": true, "it": true, "xit": true, + "specify": true, "stress": true, "waitsForPromise": true, "oneTick": true, diff --git a/lib/error-boundary.js b/lib/error-boundary.js index 16f320a934..be06024949 100644 --- a/lib/error-boundary.js +++ b/lib/error-boundary.js @@ -1,20 +1,25 @@ import React from 'react'; +import PropTypes from 'prop-types'; export default class ErrorBoundary extends React.Component { + static propTypes = { + children: PropTypes.any, + }; + constructor(props) { super(props); - this.state = { hasError: false, error: null, errorInfo: null }; + this.state = {hasError: false, error: null, errorInfo: null}; } static getDerivedStateFromError(error) { // Update state so the next render will show the fallback UI. - return { hasError: true }; + return {hasError: true}; } componentDidCatch(error, errorInfo) { this.setState({ - error: error, - errorInfo: errorInfo + error, + errorInfo, }); // You can also log the error to an error reporting service // console.log({error, errorInfo}); diff --git a/lib/github-package.js b/lib/github-package.js index 7120639f89..7cd1ae804f 100644 --- a/lib/github-package.js +++ b/lib/github-package.js @@ -603,6 +603,7 @@ export default class GithubPackage { } } +// eslint-disable-next-line no-unused-vars function pathForPaneItem(paneItem) { if (!paneItem) { return null; diff --git a/test/atom/decoration.test.js b/test/atom/decoration.test.js index bd9a663b23..1160f866fd 100644 --- a/test/atom/decoration.test.js +++ b/test/atom/decoration.test.js @@ -136,7 +136,7 @@ describe('Decoration', function() { const onError = function(e) { errors.push(e.error); e.preventDefault(); - } + }; beforeEach(function() { errors = []; @@ -166,6 +166,7 @@ describe('Decoration', function() { } else { window.removeEventListener('error', onError); for (const error of errors) { + // eslint-disable-next-line no-console console.error(error); } assert.fail(); diff --git a/test/atom/marker.test.js b/test/atom/marker.test.js index cde3f6e9c6..38410f333a 100644 --- a/test/atom/marker.test.js +++ b/test/atom/marker.test.js @@ -143,7 +143,7 @@ describe('Marker', function() { const onError = function(e) { errors.push(e.error); e.preventDefault(); - } + }; beforeEach(function() { errors = []; @@ -164,6 +164,7 @@ describe('Marker', function() { } else { window.removeEventListener('error', onError); for (const error of errors) { + // eslint-disable-next-line no-console console.error(error); } assert.fail(); diff --git a/test/containers/issueish-search-container.test.js b/test/containers/issueish-search-container.test.js index 743226b3d0..44b93a651e 100644 --- a/test/containers/issueish-search-container.test.js +++ b/test/containers/issueish-search-container.test.js @@ -90,14 +90,14 @@ describe('IssueishSearchContainer', function() { context('when the query errors', function() { let stub; // Consumes the failing Relay Query console error - before(function () { - stub = sinon.stub(console, "error"); + before(function() { + stub = sinon.stub(console, 'error'); + // eslint-disable-next-line no-console console.error.withArgs( - "Error encountered in subquery", - sinon.match.defined.and( - sinon.match.hasNested("errors[0].message", sinon.match("uh oh")) - ) + 'Error encountered in subquery', + sinon.match.defined.and(sinon.match.hasNested('errors[0].message', sinon.match('uh oh'))), ).callsFake(() => {}); + // eslint-disable-next-line no-console console.error.callThrough(); }); From 98adefc2a0b62ca74503bc3440909ac6457f9330 Mon Sep 17 00:00:00 2001 From: wadeuk3 Date: Thu, 17 Oct 2019 13:58:44 -0500 Subject: [PATCH 45/64] update to not prefer absent context from preferred/saved state work dir --- lib/github-package.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/github-package.js b/lib/github-package.js index 7cd1ae804f..96c6e60081 100644 --- a/lib/github-package.js +++ b/lib/github-package.js @@ -540,7 +540,11 @@ export default class GithubPackage { if (savedState.activeRepositoryPath) { // Preferred git directory (the preferred directory or the last serialized directory). - return this.contextPool.getContext(savedState.activeRepositoryPath); + const stateContext = this.contextPool.getContext(savedState.activeRepositoryPath); + // If the context exists chose it, else continue. + if (stateContext.isPresent()) { + return stateContext; + } } const projectPaths = this.project.getPaths(); From 083f650bef163efff3007266a7fca19f5ca3c5f7 Mon Sep 17 00:00:00 2001 From: wadeuk3 Date: Thu, 17 Oct 2019 13:59:20 -0500 Subject: [PATCH 46/64] update some test for github-package --- test/github-package.test.js | 174 +++++++++++++----------------------- 1 file changed, 60 insertions(+), 114 deletions(-) diff --git a/test/github-package.test.js b/test/github-package.test.js index 3ba12e6289..6b1c51ef2e 100644 --- a/test/github-package.test.js +++ b/test/github-package.test.js @@ -153,70 +153,68 @@ describe('GithubPackage', function() { assert.isTrue(contextPool.getContext(workdirPath1).isPresent()); assert.isTrue(contextPool.getContext(workdirPath2).isPresent()); assert.isTrue(contextPool.getContext(nonRepositoryPath).isPresent()); - - assert.isTrue(githubPackage.getActiveRepository().isUndetermined()); }); - it('uses an active model from a single preexisting project', async function() { - const workdirPath = await cloneRepository('three-files'); - project.setPaths([workdirPath]); + context('uses an active model', function() { + specify('from a single preexisting project', async function() { + const workdirPath = await cloneRepository('three-files'); + project.setPaths([workdirPath]); - await contextUpdateAfter(() => githubPackage.activate()); + await contextUpdateAfter(() => githubPackage.activate()); - const context = contextPool.getContext(workdirPath); - assert.isTrue(context.isPresent()); + const context = contextPool.getContext(workdirPath); + assert.isTrue(context.isPresent()); - assert.strictEqual(context.getRepository(), githubPackage.getActiveRepository()); - assert.strictEqual(context.getResolutionProgress(), githubPackage.getActiveResolutionProgress()); - assert.equal(githubPackage.getActiveWorkdir(), workdirPath); - }); + assert.strictEqual(context.getRepository(), githubPackage.getActiveRepository()); + assert.strictEqual(context.getResolutionProgress(), githubPackage.getActiveResolutionProgress()); + assert.equal(githubPackage.getActiveWorkdir(), workdirPath); + }); - it('uses an active model from a preexisting active pane item', async function() { - const [workdirPath1, workdirPath2] = await Promise.all([ - cloneRepository('three-files'), - cloneRepository('three-files'), - ]); - project.setPaths([workdirPath1, workdirPath2]); - await workspace.open(path.join(workdirPath2, 'a.txt')); + specify('from the first working directory when multiple are availible and no preference is set', async function() { + const [workdirPath1, workdirPath2] = await Promise.all([ + cloneRepository('three-files'), + cloneRepository('three-files'), + ]); + project.setPaths([workdirPath1, workdirPath2]); - await contextUpdateAfter(() => githubPackage.activate()); + await contextUpdateAfter(() => githubPackage.activate()); - const context = contextPool.getContext(workdirPath2); - assert.isTrue(context.isPresent()); - assert.strictEqual(context.getRepository(), githubPackage.getActiveRepository()); - assert.strictEqual(context.getResolutionProgress(), githubPackage.getActiveResolutionProgress()); - assert.equal(githubPackage.getActiveWorkdir(), workdirPath2); - }); - - it('uses an active model from serialized state', async function() { - const [workdirPath1, workdirPath2, workdirPath3] = await Promise.all([ - cloneRepository('three-files'), - cloneRepository('three-files'), - cloneRepository('three-files'), - ]); - project.setPaths([workdirPath1, workdirPath2, workdirPath3]); - - await contextUpdateAfter(() => githubPackage.activate({ - activeRepositoryPath: workdirPath2, - })); + const context = contextPool.getContext(workdirPath1); + assert.isTrue(context.isPresent()); + assert.strictEqual(context.getRepository(), githubPackage.getActiveRepository()); + assert.strictEqual(context.getResolutionProgress(), githubPackage.getActiveResolutionProgress()); + assert.equal(githubPackage.getActiveWorkdir(), workdirPath1); + }); - const context = contextPool.getContext(workdirPath2); - assert.isTrue(context.isPresent()); - assert.strictEqual(context.getRepository(), githubPackage.getActiveRepository()); - assert.strictEqual(context.getResolutionProgress(), githubPackage.getActiveResolutionProgress()); - assert.equal(githubPackage.getActiveWorkdir(), workdirPath2); + specify('from serialized state', async function() { + const [workdirPath1, workdirPath2, workdirPath3] = await Promise.all([ + cloneRepository('three-files'), + cloneRepository('three-files'), + cloneRepository('three-files'), + ]); + project.setPaths([workdirPath1, workdirPath2, workdirPath3]); + + await contextUpdateAfter(() => githubPackage.activate({ + activeRepositoryPath: workdirPath2, + })); + + const context = contextPool.getContext(workdirPath2); + assert.isTrue(context.isPresent()); + assert.strictEqual(context.getRepository(), githubPackage.getActiveRepository()); + assert.strictEqual(context.getResolutionProgress(), githubPackage.getActiveResolutionProgress()); + assert.equal(githubPackage.getActiveWorkdir(), workdirPath2); + }); }); - it('prefers the active model from an active pane item to serialized state', async function() { + it('prefers the active model from serialized state to first working directory', async function() { const [workdirPath1, workdirPath2] = await Promise.all([ cloneRepository('three-files'), cloneRepository('three-files'), ]); project.setPaths([workdirPath1, workdirPath2]); - await workspace.open(path.join(workdirPath2, 'b.txt')); await contextUpdateAfter(() => githubPackage.activate({ - activeRepositoryPath: workdirPath1, + activeRepositoryPath: workdirPath2, })); const context = contextPool.getContext(workdirPath2); @@ -226,7 +224,7 @@ describe('GithubPackage', function() { assert.equal(githubPackage.getActiveWorkdir(), workdirPath2); }); - it('prefers the active model from a single project to serialized state', async function() { + it('prefers the active model from a single project to non-active serialized state', async function() { const [workdirPath1, workdirPath2] = await Promise.all([ cloneRepository('three-files'), cloneRepository('three-files'), @@ -254,9 +252,9 @@ describe('GithubPackage', function() { project.setPaths([workdirMergeConflict, workdirNoConflict, nonRepositoryPath]); await contextUpdateAfter(() => githubPackage.activate()); - // Open a file in the merge conflict repository. - await workspace.open(path.join(workdirMergeConflict, 'modified-on-both-ours.txt')); - await githubPackage.scheduleActiveContextUpdate(); + // Switch to the merge conflict repository. + // Equivalent of user choosing a different project + await githubPackage.scheduleActiveContextUpdate({activeRepositoryPath: workdirMergeConflict}); const resolutionMergeConflict = contextPool.getContext(workdirMergeConflict).getResolutionProgress(); await assert.strictEqual(githubPackage.getActiveResolutionProgress(), resolutionMergeConflict); @@ -264,23 +262,23 @@ describe('GithubPackage', function() { // Record some resolution progress to recall later resolutionMergeConflict.reportMarkerCount('modified-on-both-ours.txt', 3); - // Open a file in the non-merge conflict repository. - await workspace.open(path.join(workdirNoConflict, 'b.txt')); - await githubPackage.scheduleActiveContextUpdate(); + // Switch to the non-merge conflict repository. + // Equivalent of user choosing a different project + await githubPackage.scheduleActiveContextUpdate({activeRepositoryPath: workdirNoConflict}); const resolutionNoConflict = contextPool.getContext(workdirNoConflict).getResolutionProgress(); assert.strictEqual(githubPackage.getActiveResolutionProgress(), resolutionNoConflict); assert.isTrue(githubPackage.getActiveResolutionProgress().isEmpty()); - // Open a file in the workdir with no repository. - await workspace.open(path.join(nonRepositoryPath, 'c.txt')); - await githubPackage.scheduleActiveContextUpdate(); + // Switch to the workdir with no repository. + // Equivalent of user choosing a different project + await githubPackage.scheduleActiveContextUpdate({activeRepositoryPath: nonRepositoryPath}); assert.isTrue(githubPackage.getActiveResolutionProgress().isEmpty()); - // Re-open a file in the merge conflict repository. - await workspace.open(path.join(workdirMergeConflict, 'modified-on-both-theirs.txt')); - await githubPackage.scheduleActiveContextUpdate(); + // Switch back to the merge conflict repository. + // Equivalent of user choosing a different project + await githubPackage.scheduleActiveContextUpdate({activeRepositoryPath: workdirMergeConflict}); assert.strictEqual(githubPackage.getActiveResolutionProgress(), resolutionMergeConflict); assert.isFalse(githubPackage.getActiveResolutionProgress().isEmpty()); @@ -403,57 +401,6 @@ describe('GithubPackage', function() { }); }); - describe('when the active pane item changes', function() { - it('becomes the active context', async function() { - const [workdirPath1, workdirPath2] = await Promise.all([ - cloneRepository('three-files'), - cloneRepository('three-files'), - ]); - project.setPaths([workdirPath1, workdirPath2]); - - await contextUpdateAfter(() => githubPackage.activate()); - - const repository2 = contextPool.getContext(workdirPath2).getRepository(); - assert.isTrue(githubPackage.getActiveRepository().isUndetermined()); - - await contextUpdateAfter(() => workspace.open(path.join(workdirPath2, 'b.txt'))); - - assert.strictEqual(githubPackage.getActiveRepository(), repository2); - }); - - it('adds a new context if not in a project', async function() { - const [workdirPath1, workdirPath2] = await Promise.all([ - cloneRepository('three-files'), - cloneRepository('three-files'), - ]); - project.setPaths([workdirPath1]); - - await contextUpdateAfter(() => githubPackage.activate()); - - assert.isFalse(contextPool.getContext(workdirPath2).isPresent()); - - await contextUpdateAfter(() => workspace.open(path.join(workdirPath2, 'c.txt'))); - - assert.equal(githubPackage.getActiveWorkdir(), workdirPath2); - assert.isTrue(contextPool.getContext(workdirPath2).isPresent()); - }); - - it('removes a context if not in a project', async function() { - const [workdirPath1, workdirPath2] = await Promise.all([ - cloneRepository('three-files'), - cloneRepository('three-files'), - ]); - project.setPaths([workdirPath1]); - await contextUpdateAfter(() => githubPackage.activate()); - - await contextUpdateAfter(() => workspace.open(path.join(workdirPath2, 'c.txt'))); - assert.isTrue(contextPool.getContext(workdirPath2).isPresent()); - - await contextUpdateAfter(() => workspace.getActivePane().destroyActiveItem()); - assert.isFalse(contextPool.getContext(workdirPath2).isPresent()); - }); - }); - describe('scheduleActiveContextUpdate()', function() { beforeEach(function() { // Necessary since we skip activate() @@ -461,20 +408,19 @@ describe('GithubPackage', function() { githubPackage.useLegacyPanels = !workspace.getLeftDock; }); - it('prefers the context of the active pane item', async function() { + it('prefers the context of the first working directory', async function() { const [workdirPath1, workdirPath2, workdirPath3] = await Promise.all([ cloneRepository('three-files'), cloneRepository('three-files'), cloneRepository('three-files'), ]); - project.setPaths([workdirPath1]); - await workspace.open(path.join(workdirPath2, 'a.txt')); + project.setPaths([workdirPath1, workdirPath2]); await githubPackage.scheduleActiveContextUpdate({ activeRepositoryPath: workdirPath3, }); - assert.equal(githubPackage.getActiveWorkdir(), workdirPath2); + assert.equal(githubPackage.getActiveWorkdir(), workdirPath1); }); it('uses an absent context when the active item is not in a git repository', async function() { From 5a0fd753e0c9994bd7041f16f9cd186a7dadacc4 Mon Sep 17 00:00:00 2001 From: wadeuk3 Date: Thu, 17 Oct 2019 17:24:25 -0500 Subject: [PATCH 47/64] remove unapplicable tests --- test/github-package.test.js | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/test/github-package.test.js b/test/github-package.test.js index 6b1c51ef2e..b08a12e56d 100644 --- a/test/github-package.test.js +++ b/test/github-package.test.js @@ -423,39 +423,6 @@ describe('GithubPackage', function() { assert.equal(githubPackage.getActiveWorkdir(), workdirPath1); }); - it('uses an absent context when the active item is not in a git repository', async function() { - const nonRepositoryPath = await fs.realpath(temp.mkdirSync()); - const workdir = await cloneRepository('three-files'); - project.setPaths([nonRepositoryPath, workdir]); - await fs.writeFile(path.join(nonRepositoryPath, 'a.txt'), 'stuff', {encoding: 'utf8'}); - - await workspace.open(path.join(nonRepositoryPath, 'a.txt')); - - await githubPackage.scheduleActiveContextUpdate(); - - assert.isTrue(githubPackage.getActiveRepository().isAbsent()); - }); - - it('uses the context of the PaneItem active in the workspace center', async function() { - if (!workspace.getLeftDock) { - this.skip(); - } - - const [workdir0, workdir1] = await Promise.all([ - cloneRepository('three-files'), - cloneRepository('three-files'), - ]); - project.setPaths([workdir1]); - - await workspace.open(path.join(workdir0, 'a.txt')); - commands.dispatch(atomEnv.views.getView(workspace), 'tree-view:toggle-focus'); - workspace.getLeftDock().activate(); - - await githubPackage.scheduleActiveContextUpdate(); - - assert.equal(githubPackage.getActiveWorkdir(), workdir0); - }); - it('uses the context of a single open project', async function() { const [workdirPath1, workdirPath2] = await Promise.all([ cloneRepository('three-files'), From 35e0df5ed9ef0629def1e53941fac9e8490a6cae Mon Sep 17 00:00:00 2001 From: wadeuk3 Date: Thu, 17 Oct 2019 17:30:58 -0500 Subject: [PATCH 48/64] remove unnecessary test case step --- test/github-package.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/github-package.test.js b/test/github-package.test.js index b08a12e56d..7423757b37 100644 --- a/test/github-package.test.js +++ b/test/github-package.test.js @@ -509,7 +509,6 @@ describe('GithubPackage', function() { const symlinkPath = (await fs.realpath(temp.mkdirSync())) + '-symlink'; fs.symlinkSync(workdirPath, symlinkPath); project.setPaths([symlinkPath]); - await workspace.open(path.join(symlinkPath, 'a.txt')); await githubPackage.scheduleActiveContextUpdate(); await assert.async.isOk(githubPackage.getActiveRepository()); From e7f5ec86e9b227f50b91946579531bf67f4227fd Mon Sep 17 00:00:00 2001 From: wadeuk3 Date: Thu, 17 Oct 2019 17:55:05 -0500 Subject: [PATCH 49/64] simplify rendering header --- lib/views/git-tab-view.js | 19 ++++++++++++++----- lib/views/github-tab-view.js | 17 ++++++++++++----- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/lib/views/git-tab-view.js b/lib/views/git-tab-view.js index 63253566e8..db4acaadbb 100644 --- a/lib/views/git-tab-view.js +++ b/lib/views/git-tab-view.js @@ -90,6 +90,7 @@ export default class GitTabView extends React.Component { if (this.props.repository.isTooLarge()) { return (
+ {renderHeader()}

Too many changes

@@ -104,6 +105,7 @@ export default class GitTabView extends React.Component { !isValidWorkdir(this.props.repository.getWorkingDirectoryPath())) { return (
+ {renderHeader()}

Unsupported directory

@@ -125,6 +127,7 @@ export default class GitTabView extends React.Component { return (
+ {renderHeader()}

Create Repository

@@ -143,11 +146,7 @@ export default class GitTabView extends React.Component { className={cx('github-Git', {'is-loading': isLoading})} tabIndex="-1" ref={this.props.refRoot.setter}> - this.props.changeProjectWorkingDirectory(e.target.value)} - currentProject={this.props.workingDirectoryPath} - projectPaths={this.props.project.getPaths()} - /> + {renderHeader()} this.props.changeProjectWorkingDirectory(e.target.value)} + currentProject={this.props.workingDirectoryPath} + projectPaths={this.props.project.getPaths()} + /> + ); + } + componentWillUnmount() { this.subscriptions.dispose(); } diff --git a/lib/views/github-tab-view.js b/lib/views/github-tab-view.js index 3fef5aabb1..ab10007375 100644 --- a/lib/views/github-tab-view.js +++ b/lib/views/github-tab-view.js @@ -36,11 +36,7 @@ export default class GitHubTabView extends React.Component { render() { return (
- this.props.changeProjectWorkingDirectory(e.target.value)} - currentProject={this.props.workingDirectory} - projectPaths={this.props.project.getPaths()} - /> + {renderHeader()}
{this.renderRemote()}
@@ -89,6 +85,7 @@ export default class GitHubTabView extends React.Component { // TODO: display a view that lets you create a repository on GitHub return (
+ {renderHeader()}

No Remotes

@@ -97,4 +94,14 @@ export default class GitHubTabView extends React.Component {
); } + + renderHeader() { + return ( + this.props.changeProjectWorkingDirectory(e.target.value)} + currentProject={this.props.workingDirectoryPath} + projectPaths={this.props.project.getPaths()} + /> + ); + } } From d64c0791f4116affd478aeba40c787e09df368ca Mon Sep 17 00:00:00 2001 From: wadeuk3 Date: Thu, 17 Oct 2019 18:54:45 -0500 Subject: [PATCH 50/64] clean up header render --- lib/views/git-tab-view.js | 135 ++++++++++++++++++++--------------- lib/views/github-tab-view.js | 5 +- test/github-package.test.js | 2 +- 3 files changed, 80 insertions(+), 62 deletions(-) diff --git a/lib/views/git-tab-view.js b/lib/views/git-tab-view.js index db4acaadbb..fb4c75374f 100644 --- a/lib/views/git-tab-view.js +++ b/lib/views/git-tab-view.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {Fragment} from 'react'; import PropTypes from 'prop-types'; import cx from 'classnames'; import {CompositeDisposable} from 'atom'; @@ -87,66 +87,95 @@ export default class GitTabView extends React.Component { } render() { + let type = 'default'; + let isEmpty = false; + let isLoading = false; if (this.props.repository.isTooLarge()) { + type = 'is-too-large'; + isEmpty = true; + } else if (this.props.repository.hasDirectory() && + !isValidWorkdir(this.props.repository.getWorkingDirectoryPath())) { + type = 'unsupported-dir'; + isEmpty = true; + } else if (this.props.repository.showGitTabInit()) { + type = 'no-repo'; + isEmpty = true; + } else if (this.props.isLoading || this.props.repository.showGitTabLoading()) { + isLoading = true; + } + + return ( +
+ {this.renderHeader()} + {this.renderBody(type)} +
+ ); + } + + renderHeader() { + return ( + this.props.changeProjectWorkingDirectory(e.target.value)} + currentProject={this.props.workingDirectoryPath} + projectPaths={this.props.project.getPaths()} + /> + ); + } + + renderBody(type) { + switch (type) { + case 'is-too-large': return ( -
- {renderHeader()} -
-
-

Too many changes

-
- The repository at {this.props.workingDirectoryPath} has too many changed files - to display in Atom. Ensure that you have set up an appropriate .gitignore file. -
+
+
+

Too many changes

+
+ The repository at {this.props.workingDirectoryPath} has too many changed files + to display in Atom. Ensure that you have set up an appropriate .gitignore file.
); - } else if (this.props.repository.hasDirectory() && - !isValidWorkdir(this.props.repository.getWorkingDirectoryPath())) { + case 'unsupported-dir': return ( -
- {renderHeader()} -
-
-

Unsupported directory

-
- Atom does not support managing Git repositories in your home or root directories. -
+
+
+

Unsupported directory

+
+ Atom does not support managing Git repositories in your home or root directories.
); - } else if (this.props.repository.showGitTabInit()) { - const inProgress = this.props.repository.showGitTabInitInProgress(); - const message = this.props.repository.hasDirectory() - ? - ( - Initialize {this.props.workingDirectoryPath} with a - Git repository - ) - : Initialize a new project directory with a Git repository; - + case 'no-repo': return ( -
- {renderHeader()} -
-
-

Create Repository

-
{message}
- +
+
+

Create Repository

+
+ { + this.props.repository.hasDirectory() + ? + ( + Initialize {this.props.workingDirectoryPath} with a + Git repository + ) + : Initialize a new project directory with a Git repository + }
+
); - } else { - const isLoading = this.props.isLoading || this.props.repository.showGitTabLoading(); - + default: return ( -
- {renderHeader()} + -
+ ); } } - renderHeader() { - return ( - this.props.changeProjectWorkingDirectory(e.target.value)} - currentProject={this.props.workingDirectoryPath} - projectPaths={this.props.project.getPaths()} - /> - ); - } - componentWillUnmount() { this.subscriptions.dispose(); } diff --git a/lib/views/github-tab-view.js b/lib/views/github-tab-view.js index ab10007375..bcf3786196 100644 --- a/lib/views/github-tab-view.js +++ b/lib/views/github-tab-view.js @@ -36,7 +36,7 @@ export default class GitHubTabView extends React.Component { render() { return (
- {renderHeader()} + {this.renderHeader()}
{this.renderRemote()}
@@ -85,7 +85,6 @@ export default class GitHubTabView extends React.Component { // TODO: display a view that lets you create a repository on GitHub return (
- {renderHeader()}

No Remotes

@@ -99,7 +98,7 @@ export default class GitHubTabView extends React.Component { return ( this.props.changeProjectWorkingDirectory(e.target.value)} - currentProject={this.props.workingDirectoryPath} + currentProject={this.props.workingDirectory} projectPaths={this.props.project.getPaths()} /> ); diff --git a/test/github-package.test.js b/test/github-package.test.js index 7423757b37..9ea390e5a9 100644 --- a/test/github-package.test.js +++ b/test/github-package.test.js @@ -7,7 +7,7 @@ import {cloneRepository, disableFilesystemWatchers} from './helpers'; import {fileExists, getTempDir} from '../lib/helpers'; import GithubPackage from '../lib/github-package'; -describe('GithubPackage', function() { +describe.only('GithubPackage', function() { let atomEnv, workspace, project, commands, notificationManager, grammars, config, keymaps; let confirm, tooltips, styles; let getLoadSettings, currentWindow, configDirPath, deserializers; From e1e77957f3ab6b8ec8496a7fc1724d85b6744e08 Mon Sep 17 00:00:00 2001 From: wadeuk3 Date: Thu, 17 Oct 2019 23:15:04 -0500 Subject: [PATCH 51/64] undo test nesting --- test/github-package.test.js | 81 ++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 42 deletions(-) diff --git a/test/github-package.test.js b/test/github-package.test.js index 9ea390e5a9..dabaf342c6 100644 --- a/test/github-package.test.js +++ b/test/github-package.test.js @@ -155,55 +155,52 @@ describe.only('GithubPackage', function() { assert.isTrue(contextPool.getContext(nonRepositoryPath).isPresent()); }); - context('uses an active model', function() { - specify('from a single preexisting project', async function() { - const workdirPath = await cloneRepository('three-files'); - project.setPaths([workdirPath]); + it('uses an active model from a single preexisting project', async function() { + const workdirPath = await cloneRepository('three-files'); + project.setPaths([workdirPath]); - await contextUpdateAfter(() => githubPackage.activate()); + await contextUpdateAfter(() => githubPackage.activate()); - const context = contextPool.getContext(workdirPath); - assert.isTrue(context.isPresent()); + const context = contextPool.getContext(workdirPath); + assert.isTrue(context.isPresent()); - assert.strictEqual(context.getRepository(), githubPackage.getActiveRepository()); - assert.strictEqual(context.getResolutionProgress(), githubPackage.getActiveResolutionProgress()); - assert.equal(githubPackage.getActiveWorkdir(), workdirPath); - }); + assert.strictEqual(context.getRepository(), githubPackage.getActiveRepository()); + assert.strictEqual(context.getResolutionProgress(), githubPackage.getActiveResolutionProgress()); + assert.equal(githubPackage.getActiveWorkdir(), workdirPath); + }); - specify('from the first working directory when multiple are availible and no preference is set', async function() { - const [workdirPath1, workdirPath2] = await Promise.all([ - cloneRepository('three-files'), - cloneRepository('three-files'), - ]); - project.setPaths([workdirPath1, workdirPath2]); + it('uses an active model from the first working directory when multiple are availible and no preference is set', async function() { + const [workdirPath1, workdirPath2] = await Promise.all([ + cloneRepository('three-files'), + cloneRepository('three-files'), + ]); + project.setPaths([workdirPath1, workdirPath2]); - await contextUpdateAfter(() => githubPackage.activate()); + await contextUpdateAfter(() => githubPackage.activate()); - const context = contextPool.getContext(workdirPath1); - assert.isTrue(context.isPresent()); - assert.strictEqual(context.getRepository(), githubPackage.getActiveRepository()); - assert.strictEqual(context.getResolutionProgress(), githubPackage.getActiveResolutionProgress()); - assert.equal(githubPackage.getActiveWorkdir(), workdirPath1); - }); + const context = contextPool.getContext(workdirPath1); + assert.isTrue(context.isPresent()); + assert.strictEqual(context.getRepository(), githubPackage.getActiveRepository()); + assert.strictEqual(context.getResolutionProgress(), githubPackage.getActiveResolutionProgress()); + assert.equal(githubPackage.getActiveWorkdir(), workdirPath1); + }); - specify('from serialized state', async function() { - const [workdirPath1, workdirPath2, workdirPath3] = await Promise.all([ - cloneRepository('three-files'), - cloneRepository('three-files'), - cloneRepository('three-files'), - ]); - project.setPaths([workdirPath1, workdirPath2, workdirPath3]); - - await contextUpdateAfter(() => githubPackage.activate({ - activeRepositoryPath: workdirPath2, - })); - - const context = contextPool.getContext(workdirPath2); - assert.isTrue(context.isPresent()); - assert.strictEqual(context.getRepository(), githubPackage.getActiveRepository()); - assert.strictEqual(context.getResolutionProgress(), githubPackage.getActiveResolutionProgress()); - assert.equal(githubPackage.getActiveWorkdir(), workdirPath2); - }); + it('uses an active model from serialized state', async function() { + const [workdirPath1, workdirPath2, workdirPath3] = await Promise.all([ + cloneRepository('three-files'), + cloneRepository('three-files'), + cloneRepository('three-files'), + ]); + project.setPaths([workdirPath1, workdirPath2, workdirPath3]); + + await contextUpdateAfter(() => githubPackage.activate({ + activeRepositoryPath: workdirPath2, + })); + const context = contextPool.getContext(workdirPath2); + assert.isTrue(context.isPresent()); + assert.strictEqual(context.getRepository(), githubPackage.getActiveRepository()); + assert.strictEqual(context.getResolutionProgress(), githubPackage.getActiveResolutionProgress()); + assert.equal(githubPackage.getActiveWorkdir(), workdirPath2); }); it('prefers the active model from serialized state to first working directory', async function() { From 63cd27e7dccded77975bbd1924573bfe511a2d0a Mon Sep 17 00:00:00 2001 From: wadeuk3 Date: Fri, 18 Oct 2019 00:08:01 -0500 Subject: [PATCH 52/64] remove only --- test/github-package.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/github-package.test.js b/test/github-package.test.js index dabaf342c6..6780343bb3 100644 --- a/test/github-package.test.js +++ b/test/github-package.test.js @@ -7,7 +7,7 @@ import {cloneRepository, disableFilesystemWatchers} from './helpers'; import {fileExists, getTempDir} from '../lib/helpers'; import GithubPackage from '../lib/github-package'; -describe.only('GithubPackage', function() { +describe('GithubPackage', function() { let atomEnv, workspace, project, commands, notificationManager, grammars, config, keymaps; let confirm, tooltips, styles; let getLoadSettings, currentWindow, configDirPath, deserializers; From 571a095bba431bfdf7b009530f03645ba5741edf Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Tue, 22 Oct 2019 17:25:58 -0500 Subject: [PATCH 53/64] revert globals --- .eslintrc | 2 -- 1 file changed, 2 deletions(-) diff --git a/.eslintrc b/.eslintrc index cacbbfa162..0c9c6128d0 100644 --- a/.eslintrc +++ b/.eslintrc @@ -36,10 +36,8 @@ "describe": true, "fdescribe": true, "xdescribe": true, - "context": true, "it": true, "xit": true, - "specify": true, "stress": true, "waitsForPromise": true, "oneTick": true, From 73de3502ecae624c487e40d950386d6a317a6104 Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Tue, 22 Oct 2019 17:27:53 -0500 Subject: [PATCH 54/64] replace context with describe --- test/atom/decoration.test.js | 2 +- test/atom/marker.test.js | 2 +- test/containers/issueish-search-container.test.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/atom/decoration.test.js b/test/atom/decoration.test.js index 1160f866fd..f8f3c1753f 100644 --- a/test/atom/decoration.test.js +++ b/test/atom/decoration.test.js @@ -129,7 +129,7 @@ describe('Decoration', function() { assert.isNull(editor.gutterWithName(gutterName)); }); - context('throws an error', function() { + describe('throws an error', function() { let errors; // This consumes the errors rather than printing them to console. diff --git a/test/atom/marker.test.js b/test/atom/marker.test.js index 38410f333a..be121c0df7 100644 --- a/test/atom/marker.test.js +++ b/test/atom/marker.test.js @@ -136,7 +136,7 @@ describe('Marker', function() { assert.strictEqual(instance.markerHolder.get(), external); }); - context('fails on construction', function() { + describe('fails on construction', function() { let errors; // This consumes the errors rather than printing them to console. diff --git a/test/containers/issueish-search-container.test.js b/test/containers/issueish-search-container.test.js index 44b93a651e..f3763b17d5 100644 --- a/test/containers/issueish-search-container.test.js +++ b/test/containers/issueish-search-container.test.js @@ -87,7 +87,7 @@ describe('IssueishSearchContainer', function() { await promise; }); - context('when the query errors', function() { + describe('when the query errors', function() { let stub; // Consumes the failing Relay Query console error before(function() { From 52c70d9f8211a2d8b6393666e34ed408d3077240 Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Tue, 22 Oct 2019 18:14:16 -0500 Subject: [PATCH 55/64] update error catching --- test/atom/decoration.test.js | 25 ++++++++----------------- test/atom/marker.test.js | 23 +++++++---------------- 2 files changed, 15 insertions(+), 33 deletions(-) diff --git a/test/atom/decoration.test.js b/test/atom/decoration.test.js index f8f3c1753f..0ac49e0d48 100644 --- a/test/atom/decoration.test.js +++ b/test/atom/decoration.test.js @@ -9,7 +9,7 @@ import Marker from '../../lib/atom/marker'; import MarkerLayer from '../../lib/atom/marker-layer'; import ErrorBoundary from '../../lib/error-boundary'; -describe('Decoration', function() { +describe.only('Decoration', function() { let atomEnv, workspace, editor, marker; beforeEach(async function() { @@ -132,25 +132,25 @@ describe('Decoration', function() { describe('throws an error', function() { let errors; - // This consumes the errors rather than printing them to console. + // This consumes the error rather than printing it to console. const onError = function(e) { - errors.push(e.error); - e.preventDefault(); + if (e.message === 'Uncaught Error: You are trying to decorate a gutter but did not supply gutterName prop.') { + errors.push(e.error); + e.preventDefault(); + } }; beforeEach(function() { errors = []; - // register error consumer window.addEventListener('error', onError); }); afterEach(function() { errors = []; - // deregister error consumer (important) window.removeEventListener('error', onError); }); - specify('if `gutterName` prop is not supplied for gutter decorations', function() { + it('if `gutterName` prop is not supplied for gutter decorations', function() { const app = ( @@ -161,16 +161,7 @@ describe('Decoration', function() { ); mount(app); - if (errors.length === 1) { - assert(errors[0], 'You are trying to decorate a gutter but did not supply gutterName prop.'); - } else { - window.removeEventListener('error', onError); - for (const error of errors) { - // eslint-disable-next-line no-console - console.error(error); - } - assert.fail(); - } + assert(errors[0], 'You are trying to decorate a gutter but did not supply gutterName prop.'); }); }); }); diff --git a/test/atom/marker.test.js b/test/atom/marker.test.js index be121c0df7..0fb62032f3 100644 --- a/test/atom/marker.test.js +++ b/test/atom/marker.test.js @@ -139,36 +139,27 @@ describe('Marker', function() { describe('fails on construction', function() { let errors; - // This consumes the errors rather than printing them to console. + // This consumes the error rather than printing it to console. const onError = function(e) { - errors.push(e.error); - e.preventDefault(); + if (e.message === 'Uncaught Error: Invalid marker ID: 67') { + errors.push(e.error); + e.preventDefault(); + } }; beforeEach(function() { errors = []; - // register error consumer window.addEventListener('error', onError); }); afterEach(function() { errors = []; - // deregister error consumer (important) window.removeEventListener('error', onError); }); - specify('if its ID is invalid', function() { + it('if its ID is invalid', function() { mount(); - if (errors.length === 1) { - assert(errors[0], 'Error: Invalid marker ID: 67'); - } else { - window.removeEventListener('error', onError); - for (const error of errors) { - // eslint-disable-next-line no-console - console.error(error); - } - assert.fail(); - } + assert(errors[0], 'Error: Invalid marker ID: 67'); }); }); From 21e025b13f1a05ebb6fc35d4c0b164637ec08847 Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Tue, 22 Oct 2019 18:24:54 -0500 Subject: [PATCH 56/64] remove only --- test/atom/decoration.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/atom/decoration.test.js b/test/atom/decoration.test.js index 0ac49e0d48..93d9ca244f 100644 --- a/test/atom/decoration.test.js +++ b/test/atom/decoration.test.js @@ -9,7 +9,7 @@ import Marker from '../../lib/atom/marker'; import MarkerLayer from '../../lib/atom/marker-layer'; import ErrorBoundary from '../../lib/error-boundary'; -describe.only('Decoration', function() { +describe('Decoration', function() { let atomEnv, workspace, editor, marker; beforeEach(async function() { From 8d6870ba4199b8fafd39ce6c5c4db52e026f941b Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Tue, 22 Oct 2019 18:35:42 -0500 Subject: [PATCH 57/64] remove unapplicable --- test/github-package.test.js | 68 ------------------------------------- 1 file changed, 68 deletions(-) diff --git a/test/github-package.test.js b/test/github-package.test.js index 496c52550b..131a56b97b 100644 --- a/test/github-package.test.js +++ b/test/github-package.test.js @@ -210,28 +210,6 @@ describe('GithubPackage', function() { }); }); - describe('with projects and an active pane', function() { - let workdirPath1, workdirPath2, context2; - beforeEach(async function() { - ([workdirPath1, workdirPath2] = await Promise.all([ - cloneRepository('three-files'), - cloneRepository('three-files'), - ])); - project.setPaths([workdirPath1, workdirPath2]); - await workspace.open(path.join(workdirPath2, 'a.txt')); - - await contextUpdateAfter(githubPackage, () => githubPackage.activate()); - context2 = contextPool.getContext(workdirPath2); - }); - - it('uses the active pane\'s context', function() { - assert.isTrue(context2.isPresent()); - assert.strictEqual(context2.getRepository(), githubPackage.getActiveRepository()); - assert.strictEqual(context2.getResolutionProgress(), githubPackage.getActiveResolutionProgress()); - assert.strictEqual(githubPackage.getActiveWorkdir(), workdirPath2); - }); - }); - describe('with projects and state', function() { let workdirPath1, workdirPath2, workdirPath3; beforeEach(async function() { @@ -664,52 +642,6 @@ describe('GithubPackage', function() { }); }); - describe('with multiple pane items', function() { - let workdirPath1, workdirPath2, context1; - - beforeEach(async function() { - ([workdirPath1, workdirPath2] = await Promise.all([ - cloneRepository('three-files'), - cloneRepository('three-files'), - ])); - project.setPaths([workdirPath2]); - - await workspace.open(path.join(workdirPath1, 'a.txt')); - commands.dispatch(atomEnv.views.getView(workspace), 'tree-view:toggle-focus'); - workspace.getLeftDock().activate(); - - await githubPackage.scheduleActiveContextUpdate(); - context1 = contextPool.getContext(workdirPath1); - }); - - it('uses the active pane\'s context', function() { - assert.isTrue(context1.isPresent()); - assert.strictEqual(context1.getRepository(), githubPackage.getActiveRepository()); - assert.strictEqual(context1.getResolutionProgress(), githubPackage.getActiveResolutionProgress()); - assert.strictEqual(githubPackage.getActiveWorkdir(), workdirPath1); - }); - }); - - describe('with an active context', function() { - let workdirPath1, workdirPath2; - beforeEach(async function() { - ([workdirPath1, workdirPath2] = await Promise.all([ - cloneRepository('three-files'), - cloneRepository('three-files'), - ])); - project.setPaths([workdirPath1, workdirPath2]); - - contextPool.set([workdirPath1, workdirPath2]); - githubPackage.setActiveContext(contextPool.getContext(workdirPath1)); - - await githubPackage.scheduleActiveContextUpdate(); - }); - - it('uses the active context', function() { - assert.strictEqual(githubPackage.getActiveWorkdir(), workdirPath1); - }); - }); - describe('with a repository project\'s subdirectory', function() { let workdirPath; beforeEach(async function() { From 558c9a03382272976348fbf288ca8bc6631239dd Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Tue, 22 Oct 2019 19:15:26 -0500 Subject: [PATCH 58/64] remove active pane listener --- lib/github-package.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/github-package.js b/lib/github-package.js index 96c6e60081..38f3bfed6d 100644 --- a/lib/github-package.js +++ b/lib/github-package.js @@ -175,12 +175,6 @@ export default class GithubPackage { return !!event.target.closest('.github-FilePatchListView').querySelector('.is-selected'); }; - const handleActivePaneChange = item => { - const activeRepository = this.getActiveRepository(); - const activeRepositoryPath = activeRepository ? activeRepository.getWorkingDirectoryPath() : null; - this.scheduleActiveContextUpdate({activeRepositoryPath}, {item}); - }; - const handleProjectPathsChange = projectPaths => { const activeRepository = this.getActiveRepository(); const activeRepositoryPath = activeRepository ? activeRepository.getWorkingDirectoryPath() : null; @@ -189,7 +183,6 @@ export default class GithubPackage { this.subscriptions.add( this.project.onDidChangePaths(handleProjectPathsChange), - this.workspace.getCenter().onDidStopChangingActivePaneItem(handleActivePaneChange), this.styleCalculator.startWatching( 'github-package-styles', ['editor.fontSize', 'editor.fontFamily', 'editor.lineHeight', 'editor.tabLength'], From f03e06adbf807a1c9b48166dfaf86bf2c7bb8c1a Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Wed, 23 Oct 2019 00:12:10 -0500 Subject: [PATCH 59/64] update all tests --- test/github-package.test.js | 116 ++++++++++++------------------------ 1 file changed, 37 insertions(+), 79 deletions(-) diff --git a/test/github-package.test.js b/test/github-package.test.js index 131a56b97b..440a22bf69 100644 --- a/test/github-package.test.js +++ b/test/github-package.test.js @@ -158,7 +158,7 @@ describe('GithubPackage', function() { atomEnv.destroy(); }); - describe('with no project, state, or active pane', function() { + describe('with no projects or state', function() { beforeEach(async function() { await contextUpdateAfter(githubPackage, () => githubPackage.activate()); }); @@ -187,7 +187,7 @@ describe('GithubPackage', function() { }); describe('with only projects', function() { - let workdirPath1, workdirPath2, nonRepositoryPath; + let workdirPath1, workdirPath2, nonRepositoryPath, context1; beforeEach(async function() { ([workdirPath1, workdirPath2, nonRepositoryPath] = await Promise.all([ cloneRepository('three-files'), @@ -197,10 +197,15 @@ describe('GithubPackage', function() { project.setPaths([workdirPath1, workdirPath2, nonRepositoryPath]); await contextUpdateAfter(githubPackage, () => githubPackage.activate()); + + context1 = contextPool.getContext(workdirPath1); }); - it('uses an undetermined repository context', function() { - assert.isTrue(githubPackage.getActiveRepository().isUndetermined()); + it('uses the first project\'s context', function() { + assert.isTrue(context1.isPresent()); + assert.strictEqual(githubPackage.getActiveWorkdir(), workdirPath1); + assert.strictEqual(context1.getRepository(), githubPackage.getActiveRepository()); + assert.strictEqual(context1.getResolutionProgress(), githubPackage.getActiveResolutionProgress()); }); it('creates contexts from preexisting projects', function() { @@ -234,31 +239,7 @@ describe('GithubPackage', function() { }); }); - describe('with projects, state, and an active pane', function() { - let workdirPath1, workdirPath2, context2; - beforeEach(async function() { - ([workdirPath1, workdirPath2] = await Promise.all([ - cloneRepository('three-files'), - cloneRepository('three-files'), - ])); - project.setPaths([workdirPath1, workdirPath2]); - await workspace.open(path.join(workdirPath2, 'b.txt')); - - await contextUpdateAfter(githubPackage, () => githubPackage.activate({ - activeRepositoryPath: workdirPath1, - })); - context2 = contextPool.getContext(workdirPath2); - }); - - it('uses the active pane\'s context', function() { - assert.isTrue(context2.isPresent()); - assert.strictEqual(context2.getRepository(), githubPackage.getActiveRepository()); - assert.strictEqual(context2.getResolutionProgress(), githubPackage.getActiveResolutionProgress()); - assert.strictEqual(githubPackage.getActiveWorkdir(), workdirPath2); - }); - }); - - describe('with 1 project and state', function() { + describe('with 1 project and absent state', function() { let workdirPath1, workdirPath2, context1; beforeEach(async function() { ([workdirPath1, workdirPath2] = await Promise.all([ @@ -412,8 +393,12 @@ describe('GithubPackage', function() { await contextUpdateAfter(githubPackage, () => githubPackage.activate()); }); - it('uses an absent context', function() { - assert.isTrue(githubPackage.getActiveRepository().isUndetermined()); + it('uses the first project\'s context', function() { + const context1 = contextPool.getContext(workdirPath1); + assert.isTrue(context1.isPresent()); + assert.strictEqual(githubPackage.getActiveWorkdir(), workdirPath1); + assert.strictEqual(context1.getRepository(), githubPackage.getActiveRepository()); + assert.strictEqual(context1.getResolutionProgress(), githubPackage.getActiveResolutionProgress()); }); it('has no contexts for projects that are not open', function() { @@ -449,19 +434,8 @@ describe('GithubPackage', function() { assert.isFalse(contextPool.getContext(workdirPath1).isPresent()); }); - it('use an absent guess repo', function() { - assert.isTrue(githubPackage.getActiveRepository().isAbsentGuess()); - }); - }); - - describe('when an active pane is opened', function() { - beforeEach(async function() { - await contextUpdateAfter(githubPackage, () => workspace.open(path.join(workdirPath2, 'b.txt'))); - }); - - it('uses the new active pane\'s context', function() { - const repository2 = contextPool.getContext(workdirPath2).getRepository(); - assert.strictEqual(githubPackage.getActiveRepository(), repository2); + it('uses an absent repo', function() { + assert.isTrue(githubPackage.getActiveRepository().isAbsent()); }); }); }); @@ -481,11 +455,12 @@ describe('GithubPackage', function() { resolutionMergeConflict.reportMarkerCount('modified-on-both-ours.txt', remainingMarkerCount); }); - describe('when opening an in-progress merge-conflict project', function() { + describe('when selecting an in-progress merge-conflict project', function() { let resolutionMergeConflict; beforeEach(async function() { - await workspace.open(path.join(workdirMergeConflict, 'modified-on-both-ours.txt')); - await githubPackage.scheduleActiveContextUpdate(); + await githubPackage.scheduleActiveContextUpdate({ + activeRepositoryPath: workdirMergeConflict, + }); resolutionMergeConflict = contextPool.getContext(workdirMergeConflict).getResolutionProgress(); }); @@ -505,8 +480,9 @@ describe('GithubPackage', function() { describe('when opening a no-conflict repository project', function() { let resolutionNoConflict; beforeEach(async function() { - await workspace.open(path.join(workdirNoConflict, 'b.txt')); - await githubPackage.scheduleActiveContextUpdate(); + await githubPackage.scheduleActiveContextUpdate({ + activeRepositoryPath: workdirNoConflict, + }); resolutionNoConflict = contextPool.getContext(workdirNoConflict).getResolutionProgress(); }); @@ -521,8 +497,9 @@ describe('GithubPackage', function() { describe('when opening a non-repository project', function() { beforeEach(async function() { - await workspace.open(path.join(nonRepositoryPath, 'c.txt')); - await githubPackage.scheduleActiveContextUpdate(); + await githubPackage.scheduleActiveContextUpdate({ + activeRepositoryPath: nonRepositoryPath, + }); }); it('has no active resolution progress', function() { @@ -531,28 +508,27 @@ describe('GithubPackage', function() { }); }); - describe('with projects, state, and an active pane', function() { - let workdirPath1, workdirPath2, workdirPath3, context2; + describe('with projects and absent state', function() { + let workdirPath1, workdirPath2, workdirPath3, context1; beforeEach(async function() { ([workdirPath1, workdirPath2, workdirPath3] = await Promise.all([ cloneRepository('three-files'), cloneRepository('three-files'), cloneRepository('three-files'), ])); - project.setPaths([workdirPath1]); - await workspace.open(path.join(workdirPath2, 'a.txt')); + project.setPaths([workdirPath1, workdirPath2]); await githubPackage.scheduleActiveContextUpdate({ activeRepositoryPath: workdirPath3, }); - context2 = contextPool.getContext(workdirPath2); + context1 = contextPool.getContext(workdirPath1); }); - it('uses the active pane\'s context', function() { - assert.isTrue(context2.isPresent()); - assert.strictEqual(context2.getRepository(), githubPackage.getActiveRepository()); - assert.strictEqual(context2.getResolutionProgress(), githubPackage.getActiveResolutionProgress()); - assert.strictEqual(githubPackage.getActiveWorkdir(), workdirPath2); + it('uses the first project\'s context', function() { + assert.isTrue(context1.isPresent()); + assert.strictEqual(context1.getRepository(), githubPackage.getActiveRepository()); + assert.strictEqual(context1.getResolutionProgress(), githubPackage.getActiveResolutionProgress()); + assert.strictEqual(githubPackage.getActiveWorkdir(), workdirPath1); }); }); @@ -625,23 +601,6 @@ describe('GithubPackage', function() { }); }); - describe('with an active pane in a non-repository project', function() { - beforeEach(async function() { - const nonRepositoryPath = await fs.realpath(temp.mkdirSync()); - const workdir = await cloneRepository('three-files'); - project.setPaths([nonRepositoryPath, workdir]); - await fs.writeFile(path.join(nonRepositoryPath, 'a.txt'), 'stuff', {encoding: 'utf8'}); - - await workspace.open(path.join(nonRepositoryPath, 'a.txt')); - - await githubPackage.scheduleActiveContextUpdate(); - }); - - it('uses and absent context', function() { - assert.isTrue(githubPackage.getActiveRepository().isAbsent()); - }); - }); - describe('with a repository project\'s subdirectory', function() { let workdirPath; beforeEach(async function() { @@ -691,7 +650,6 @@ describe('GithubPackage', function() { const symlinkPath = (await fs.realpath(temp.mkdirSync())) + '-symlink'; fs.symlinkSync(workdirPath, symlinkPath); project.setPaths([symlinkPath]); - await workspace.open(path.join(symlinkPath, 'a.txt')); await githubPackage.scheduleActiveContextUpdate(); }); From 34adef2b9887a23e7da7802a25a03f2b58a19a23 Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Wed, 23 Oct 2019 00:15:00 -0500 Subject: [PATCH 60/64] lints --- test/github-package.test.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/github-package.test.js b/test/github-package.test.js index 440a22bf69..d53ae4464b 100644 --- a/test/github-package.test.js +++ b/test/github-package.test.js @@ -141,7 +141,7 @@ describe('GithubPackage', function() { describe('activate()', function() { let atomEnv, githubPackage; - let workspace, project, config; + let project, config; let configDirPath, contextPool; beforeEach(async function() { @@ -353,8 +353,7 @@ describe('GithubPackage', function() { describe('scheduleActiveContextUpdate()', function() { let atomEnv, githubPackage; - let workspace, project, commands; - let contextPool; + let project, contextPool; beforeEach(async function() { ({ From 72eb56c9798079c0909cfc4312c06d0914fc0644 Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Wed, 23 Oct 2019 00:24:45 -0500 Subject: [PATCH 61/64] lints --- test/github-package.test.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/test/github-package.test.js b/test/github-package.test.js index d53ae4464b..dfb0308011 100644 --- a/test/github-package.test.js +++ b/test/github-package.test.js @@ -141,14 +141,12 @@ describe('GithubPackage', function() { describe('activate()', function() { let atomEnv, githubPackage; - let project, config; - let configDirPath, contextPool; + let project, config, configDirPath, contextPool; beforeEach(async function() { ({ atomEnv, githubPackage, - workspace, project, - config, configDirPath, contextPool, + project, config, configDirPath, contextPool, } = await buildAtomEnvironmentAndGithubPackage(global.buildAtomEnvironmentAndGithubPackage)); }); @@ -358,8 +356,7 @@ describe('GithubPackage', function() { beforeEach(async function() { ({ atomEnv, githubPackage, - workspace, project, commands, - contextPool, + project, contextPool, } = await buildAtomEnvironmentAndGithubPackage(global.buildAtomEnvironmentAndGithubPackage)); }); From aa2edc9a4906a0226bb986144cf3ac45d547764f Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Wed, 23 Oct 2019 15:52:30 -0500 Subject: [PATCH 62/64] add test cases --- test/views/header-view.test.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 test/views/header-view.test.js diff --git a/test/views/header-view.test.js b/test/views/header-view.test.js new file mode 100644 index 0000000000..77d9868202 --- /dev/null +++ b/test/views/header-view.test.js @@ -0,0 +1,33 @@ +import React from 'react'; +import {shallow} from 'enzyme'; +import path from 'path'; + +import HeaderView from '../../lib/views/header-view'; + +describe('HeaderView', function() { + let wrapper, select; + const path1 = 'test/path/project1'; + const path2 = '2nd-test/path/project2'; + const paths = [path1, path2]; + + beforeEach(function() { + select = sinon.spy(); + wrapper = shallow(); + }); + + it('renders an option for all given project paths', function() { + wrapper.find('option').forEach(function(node, index) { + assert.strictEqual(node.props().value, paths[index]); + assert.strictEqual(node.children().text(), path.basename(paths[index])); + }); + }); + + it('selects the current project\'s path', function() { + assert.strictEqual(wrapper.find('select').props().value, path2); + }); + + it('calls handleProjectSelect on select', function() { + wrapper.find('select').simulate('change', {target: {value: path1}}); + assert.isTrue(select.calledWith({target: {value: path1}})); + }); +}); From 265072cf7ab44c3c37a5cbad031763c954bcf153 Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Wed, 23 Oct 2019 17:01:43 -0500 Subject: [PATCH 63/64] add integration tests --- test/views/git-tab-view.test.js | 9 +++++++++ test/views/github-tab-view.test.js | 11 ++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/test/views/git-tab-view.test.js b/test/views/git-tab-view.test.js index 93710af685..c410b55c88 100644 --- a/test/views/git-tab-view.test.js +++ b/test/views/git-tab-view.test.js @@ -282,4 +282,13 @@ describe('GitTabView', function() { wrapper.instance().focusAndSelectRecentCommit(); assert.isTrue(setFocus.calledWith(GitTabView.focus.RECENT_COMMIT)); }); + + it('calls changeProjectWorkingDirectory when a project is selected', async function() { + const select = sinon.spy(); + const path = 'test/path' + const wrapper = mount(await buildApp({changeProjectWorkingDirectory: select})); + wrapper.find('.github-Project-path.input-select').simulate('change', {target: {value: path}}); + assert.isTrue(select.calledWith(path)); + wrapper.unmount(); + }); }); diff --git a/test/views/github-tab-view.test.js b/test/views/github-tab-view.test.js index 37b494df63..64636dfba4 100644 --- a/test/views/github-tab-view.test.js +++ b/test/views/github-tab-view.test.js @@ -1,5 +1,5 @@ import React from 'react'; -import {shallow} from 'enzyme'; +import {shallow, mount} from 'enzyme'; import {gitHubTabViewProps} from '../fixtures/props/github-tab-props'; import Repository from '../../lib/models/repository'; @@ -63,4 +63,13 @@ describe('GitHubTabView', function() { const wrapper = shallow(buildApp({currentRemote: nullRemote, manyRemotesAvailable: false})); assert.isTrue(wrapper.find('.github-GitHub-noRemotes').exists()); }); + + it('calls changeProjectWorkingDirectory when a project is selected', function() { + const select = sinon.spy(); + const path = 'test/path' + const wrapper = mount(buildApp({changeProjectWorkingDirectory: select})); + wrapper.find('.github-Project-path.input-select').simulate('change', {target: {value: path}}); + assert.isTrue(select.calledWith(path)); + wrapper.unmount(); + }); }); From 40df468a330899e163d61c8072588f63aec2741e Mon Sep 17 00:00:00 2001 From: wadethestealth Date: Wed, 23 Oct 2019 17:06:03 -0500 Subject: [PATCH 64/64] lint --- test/views/git-tab-view.test.js | 2 +- test/views/github-tab-view.test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/views/git-tab-view.test.js b/test/views/git-tab-view.test.js index c410b55c88..4ad44b2f71 100644 --- a/test/views/git-tab-view.test.js +++ b/test/views/git-tab-view.test.js @@ -285,7 +285,7 @@ describe('GitTabView', function() { it('calls changeProjectWorkingDirectory when a project is selected', async function() { const select = sinon.spy(); - const path = 'test/path' + const path = 'test/path'; const wrapper = mount(await buildApp({changeProjectWorkingDirectory: select})); wrapper.find('.github-Project-path.input-select').simulate('change', {target: {value: path}}); assert.isTrue(select.calledWith(path)); diff --git a/test/views/github-tab-view.test.js b/test/views/github-tab-view.test.js index 64636dfba4..c6f410cfab 100644 --- a/test/views/github-tab-view.test.js +++ b/test/views/github-tab-view.test.js @@ -66,7 +66,7 @@ describe('GitHubTabView', function() { it('calls changeProjectWorkingDirectory when a project is selected', function() { const select = sinon.spy(); - const path = 'test/path' + const path = 'test/path'; const wrapper = mount(buildApp({changeProjectWorkingDirectory: select})); wrapper.find('.github-Project-path.input-select').simulate('change', {target: {value: path}}); assert.isTrue(select.calledWith(path));