diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ed16668ac6..bbc2bc05c3 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,14 +1,3 @@ -trigger: - branches: - include: - - master - - releases/* - -pr: - branches: - include: - - "*" - jobs: - job: Linux pool: diff --git a/lib/atom/atom-text-editor.js b/lib/atom/atom-text-editor.js index abf5164906..8d180480ac 100644 --- a/lib/atom/atom-text-editor.js +++ b/lib/atom/atom-text-editor.js @@ -5,7 +5,7 @@ import {CompositeDisposable} from 'event-kit'; import RefHolder from '../models/ref-holder'; import {RefHolderPropType} from '../prop-types'; -import {autobind, extractProps} from '../helpers'; +import {extractProps} from '../helpers'; const editorUpdateProps = { mini: PropTypes.bool, @@ -28,7 +28,7 @@ export default class AtomTextEditor extends React.Component { static propTypes = { ...editorCreationProps, - workspace: PropTypes.object.isRequired, // FIXME make more specific + workspace: PropTypes.object.isRequired, didChangeCursorPosition: PropTypes.func, didAddSelection: PropTypes.func, @@ -50,7 +50,6 @@ export default class AtomTextEditor extends React.Component { constructor(props) { super(props); - autobind(this, 'observeSelections'); this.subs = new CompositeDisposable(); @@ -98,7 +97,7 @@ export default class AtomTextEditor extends React.Component { this.subs.dispose(); } - observeSelections(selection) { + observeSelections = selection => { const selectionSubs = new CompositeDisposable( selection.onDidChangeRange(this.props.didChangeSelectionRange), selection.onDidDestroy(() => { diff --git a/lib/atom/decoration.js b/lib/atom/decoration.js index 9e3049f1a2..cd3cbead27 100644 --- a/lib/atom/decoration.js +++ b/lib/atom/decoration.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import {Disposable} from 'event-kit'; import cx from 'classnames'; -import {createItem, autobind, extractProps} from '../helpers'; +import {createItem, extractProps} from '../helpers'; import {RefHolderPropType} from '../prop-types'; import {TextEditorContext} from './atom-text-editor'; import {DecorableContext} from './marker'; @@ -40,8 +40,6 @@ class BareDecoration extends React.Component { constructor(props, context) { super(props, context); - autobind(this, 'observeParents'); - this.decorationHolder = new RefHolder(); this.editorSub = new Disposable(); this.decorableSub = new Disposable(); @@ -93,7 +91,7 @@ class BareDecoration extends React.Component { } } - observeParents() { + observeParents = () => { this.decorationHolder.map(decoration => decoration.destroy()); const editorValid = this.props.editorHolder.map(editor => !editor.isDestroyed()).getOr(false); diff --git a/lib/models/repository-states/present.js b/lib/models/repository-states/present.js index c591d25c7e..830c18280e 100644 --- a/lib/models/repository-states/present.js +++ b/lib/models/repository-states/present.js @@ -43,6 +43,7 @@ export default class Present extends State { this.commitMessageTemplate = null; this.fetchInitialMessage(); + /* istanbul ignore else */ if (history) { this.discardHistory.updateHistory(history); } @@ -165,6 +166,7 @@ export default class Present extends State { keys.add(Keys.statusBundle); } + /* istanbul ignore else */ if (keys.size > 0) { this.cache.invalidate(Array.from(keys)); this.didUpdate(); @@ -557,6 +559,7 @@ export default class Present extends State { destructiveAction, partialDiscardFilePath, ); + /* istanbul ignore else */ if (snapshots) { await this.saveDiscardHistory(); } diff --git a/package-lock.json b/package-lock.json index eef33d95bb..31ec405425 100644 --- a/package-lock.json +++ b/package-lock.json @@ -244,6 +244,15 @@ "acorn": "^5.0.3" } }, + "agent-base": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", + "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, "ajv": { "version": "5.5.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", @@ -2016,15 +2025,14 @@ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "codecov": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/codecov/-/codecov-3.1.0.tgz", - "integrity": "sha512-aWQc/rtHbcWEQLka6WmBAOpV58J2TwyXqlpAQGhQaSiEUoigTTUk6lLd2vB3kXkhnDyzyH74RXfmV4dq2txmdA==", + "version": "github:codecov/codecov-node#e427d900309adb50746a39a50aa7d80071a5ddd0", + "from": "github:codecov/codecov-node#e427d90", "dev": true, "requires": { "argv": "^0.0.2", "ignore-walk": "^3.0.1", "js-yaml": "^3.12.0", - "request": "^2.87.0", + "teeny-request": "^3.7.0", "urlgrey": "^0.4.4" }, "dependencies": { @@ -2854,6 +2862,21 @@ "is-symbol": "^1.0.1" } }, + "es6-promise": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz", + "integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==", + "dev": true + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "http://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "dev": true, + "requires": { + "es6-promise": "^4.0.3" + } + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -4024,6 +4047,33 @@ "sshpk": "^1.7.0" } }, + "https-proxy-agent": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", + "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", + "dev": true, + "requires": { + "agent-base": "^4.1.0", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, "iconv-lite": { "version": "0.4.18", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz", @@ -5082,7 +5132,7 @@ }, "load-json-file": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { @@ -7693,6 +7743,25 @@ "xtend": "^4.0.0" } }, + "teeny-request": { + "version": "3.11.3", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-3.11.3.tgz", + "integrity": "sha512-CKncqSF7sH6p4rzCgkb/z/Pcos5efl0DmolzvlqRQUNcpRIruOhY9+T1FsIlyEbfWd7MsFpodROOwHYh2BaXzw==", + "dev": true, + "requires": { + "https-proxy-agent": "^2.2.1", + "node-fetch": "^2.2.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "dev": true + } + } + }, "temp": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz", diff --git a/package.json b/package.json index 33e7651c5e..5b0603a104 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "babel-plugin-istanbul": "4.1.6", "chai": "4.1.2", "chai-as-promised": "7.1.1", - "codecov": "3.1.0", + "codecov": "codecov/codecov-node#e427d90", "cross-env": "5.2.0", "cross-unzip": "0.2.1", "dedent-js": "1.0.1", diff --git a/test/atom/atom-text-editor.test.js b/test/atom/atom-text-editor.test.js index 23f764074a..a2d8cb7e78 100644 --- a/test/atom/atom-text-editor.test.js +++ b/test/atom/atom-text-editor.test.js @@ -1,5 +1,5 @@ import React from 'react'; -import {mount} from 'enzyme'; +import {mount, shallow} from 'enzyme'; import {TextBuffer} from 'atom'; import RefHolder from '../../lib/models/ref-holder'; @@ -35,6 +35,12 @@ describe('AtomTextEditor', function() { assert.isTrue(workspace.isTextEditor(app.instance().refModel.get())); }); + it('returns undefined if the current model is unavailable', function() { + const emptyHolder = new RefHolder(); + const app = shallow(); + assert.isUndefined(app.instance().getModel()); + }); + it('configures the created text editor with props', function() { mount( { + sub = repository.onDidUpdate(() => { + sub.dispose(); + resolve(); + }); + }); + + await assert.async.strictEqual(repository.getCommitMessage(), 'hai'); + }); + }); + + describe('update broadcast', function() { + it('broadcasts an update when set', async function() { + const repository = new Repository(await cloneRepository()); + await repository.getLoadPromise(); + const didUpdate = sinon.spy(); + sub = repository.onDidUpdate(didUpdate); + + repository.setCommitMessage('new message'); + assert.isTrue(didUpdate.called); + }); + + it('may suppress the update when set', async function() { + const repository = new Repository(await cloneRepository()); + await repository.getLoadPromise(); + const didUpdate = sinon.spy(); + sub = repository.onDidUpdate(didUpdate); + + repository.setCommitMessage('quietly now', {suppressUpdate: true}); + assert.isFalse(didUpdate.called); + }); + }); + describe('updateCommitMessageAfterFileSystemChange', function() { it('handles events with no `path` property', async function() { const {repository} = await wireUpObserver(); @@ -2213,6 +2320,31 @@ describe('Repository', function() { ); await assert.async.strictEqual(repository.getCommitMessage(), fs.readFileSync(templatePath, 'utf8')); }); + + it('leaves the commit message alone if the template content did not change', async function() { + const {repository, observer, subscriptions} = await wireUpObserver(); + sub = subscriptions; + await observer.start(); + + const templateOnePath = path.join(repository.getWorkingDirectoryPath(), 'the-template-0.txt'); + const templateTwoPath = path.join(repository.getWorkingDirectoryPath(), 'the-template-1.txt'); + const templateContent = 'the same'; + + await Promise.all( + [templateOnePath, templateTwoPath].map(p => fs.writeFile(p, templateContent, {encoding: 'utf8'})), + ); + + await repository.git.setConfig('commit.template', templateOnePath); + await expectEvents(repository, path.join('.git', 'config')); + await assert.async.strictEqual(repository.getCommitMessage(), 'the same'); + + repository.setCommitMessage('different'); + + await repository.git.setConfig('commit.template', templateTwoPath); + await expectEvents(repository, path.join('.git', 'config')); + assert.strictEqual(repository.getCommitMessage(), 'different'); + }); + it('updates commit message to empty string if commit.template is unset', async function() { const {repository, observer, subscriptions} = await wireUpObserver(); sub = subscriptions;