├── .github └── no-response.yml ├── .gitignore ├── .travis.yml ├── ISSUE_TEMPLATE.md ├── LICENSE.md ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── appveyor.yml ├── lib ├── incompatible-packages-component.js ├── main.js ├── status-icon-component.js └── view-uri.js ├── package.json ├── spec ├── fixtures │ └── incompatible-package │ │ ├── bad.js │ │ └── package.json ├── incompatible-packages-component-spec.js └── incompatible-packages-spec.js └── styles └── incompatible-packages.less /.github/no-response.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-no-response - https://github.com/probot/no-response 2 | 3 | # Number of days of inactivity before an issue is closed for lack of response 4 | daysUntilClose: 180 5 | 6 | # Label requiring a response 7 | responseRequiredLabel: more-information-needed 8 | 9 | # Comment to post when closing an issue for lack of response. Set to `false` to disable. 10 | closeComment: > 11 | This issue has been automatically closed because there has been no response 12 | to our request for more information from the original author. With only the 13 | information that is currently in the issue, we don't have enough information 14 | to take action. Please reach out if you have or find the answers we need so 15 | that we can investigate further. 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | npm-debug.log 3 | node_modules 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | 3 | notifications: 4 | email: 5 | on_success: never 6 | on_failure: change 7 | 8 | script: 'curl -s https://raw.githubusercontent.com/atom/ci/master/build-package.sh | sh' 9 | 10 | branches: 11 | only: 12 | - master 13 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | ### Prerequisites 10 | 11 | * [ ] Put an X between the brackets on this line if you have done all of the following: 12 | * Reproduced the problem in Safe Mode: http://flight-manual.atom.io/hacking-atom/sections/debugging/#using-safe-mode 13 | * Followed all applicable steps in the debugging guide: http://flight-manual.atom.io/hacking-atom/sections/debugging/ 14 | * Checked the FAQs on the message board for common solutions: https://discuss.atom.io/c/faq 15 | * Checked that your issue isn't already filed: https://github.com/issues?utf8=✓&q=is%3Aissue+user%3Aatom 16 | * Checked that there is not already an Atom package that provides the described functionality: https://atom.io/packages 17 | 18 | ### Description 19 | 20 | [Description of the issue] 21 | 22 | ### Steps to Reproduce 23 | 24 | 1. [First Step] 25 | 2. [Second Step] 26 | 3. [and so on...] 27 | 28 | **Expected behavior:** [What you expect to happen] 29 | 30 | **Actual behavior:** [What actually happens] 31 | 32 | **Reproduces how often:** [What percentage of the time does it reproduce?] 33 | 34 | ### Versions 35 | 36 | You can get this information from copy and pasting the output of `atom --version` and `apm --version` from the command line. Also, please include the OS and what version of the OS you're running. 37 | 38 | ### Additional Information 39 | 40 | Any additional information, configuration or data that might be necessary to reproduce the issue. 41 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 GitHub Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Requirements 2 | 3 | * Filling out the template is required. Any pull request that does not include enough information to be reviewed in a timely manner may be closed at the maintainers' discretion. 4 | * All new code requires tests to ensure against regressions 5 | 6 | ### Description of the Change 7 | 8 | 13 | 14 | ### Alternate Designs 15 | 16 | 17 | 18 | ### Benefits 19 | 20 | 21 | 22 | ### Possible Drawbacks 23 | 24 | 25 | 26 | ### Applicable Issues 27 | 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### This package is now a part of the [core Atom repository](https://github.com/atom/atom/tree/master/packages/incompatible-packages), please direct all issues and pull requests there in the future! 2 | 3 | --- 4 | 5 | # Incompatible Packages package 6 | [![OS X Build Status](https://travis-ci.org/atom/incompatible-packages.svg?branch=master)](https://travis-ci.org/atom/incompatible-packages) [![Windows Build Status](https://ci.appveyor.com/api/projects/status/neet595s038x7w70/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/incompatible-packages/branch/master) [![Dependency Status](https://david-dm.org/atom/incompatible-packages.svg)](https://david-dm.org/atom/incompatible-packages) 7 | 8 | Displays a list of installed Atom packages that have native module 9 | dependencies that are not compatible with the current version of Atom. 10 | 11 | ![](https://cloud.githubusercontent.com/assets/671378/3767534/3f099820-18ce-11e4-9fa0-feef7947aab2.png) 12 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: "{build}" 2 | 3 | platform: x64 4 | 5 | branches: 6 | only: 7 | - master 8 | 9 | clone_depth: 10 10 | 11 | skip_tags: true 12 | 13 | environment: 14 | APM_TEST_PACKAGES: 15 | 16 | matrix: 17 | - ATOM_CHANNEL: stable 18 | - ATOM_CHANNEL: beta 19 | 20 | install: 21 | - ps: Install-Product node 4 22 | 23 | build_script: 24 | - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/atom/ci/master/build-package.ps1')) 25 | 26 | test: off 27 | deploy: off 28 | -------------------------------------------------------------------------------- /lib/incompatible-packages-component.js: -------------------------------------------------------------------------------- 1 | /** @babel */ 2 | /** @jsx etch.dom */ 3 | 4 | import {BufferedProcess} from 'atom' 5 | import etch from 'etch' 6 | 7 | import VIEW_URI from './view-uri' 8 | const REBUILDING = 'rebuilding' 9 | const REBUILD_FAILED = 'rebuild-failed' 10 | const REBUILD_SUCCEEDED = 'rebuild-succeeded' 11 | 12 | export default class IncompatiblePackagesComponent { 13 | constructor (packageManager) { 14 | this.rebuildStatuses = new Map 15 | this.rebuildFailureOutputs = new Map 16 | this.rebuildInProgress = false 17 | this.rebuiltPackageCount = 0 18 | this.packageManager = packageManager 19 | this.loaded = false 20 | etch.initialize(this) 21 | 22 | if (this.packageManager.getActivePackages().length > 0) { 23 | this.populateIncompatiblePackages() 24 | } else { 25 | global.setImmediate(this.populateIncompatiblePackages.bind(this)) 26 | } 27 | 28 | this.element.addEventListener('click', (event) => { 29 | if (event.target === this.refs.rebuildButton) { 30 | this.rebuildIncompatiblePackages() 31 | } else if (event.target === this.refs.reloadButton) { 32 | atom.reload() 33 | } else if (event.target.classList.contains('view-settings')) { 34 | atom.workspace.open(`atom://config/packages/${event.target.package.name}`) 35 | } 36 | }) 37 | } 38 | 39 | update () {} 40 | 41 | render () { 42 | if (!this.loaded) { 43 | return
Loading...
44 | } 45 | 46 | return ( 47 |
48 | {this.renderHeading()} 49 | {this.renderIncompatiblePackageList()} 50 |
51 | ) 52 | } 53 | 54 | renderHeading () { 55 | if (this.incompatiblePackages.length > 0) { 56 | if (this.rebuiltPackageCount > 0) { 57 | let alertClass = 58 | (this.rebuiltPackageCount === this.incompatiblePackages.length) 59 | ? 'alert-success icon-check' 60 | : 'alert-warning icon-bug' 61 | 62 | return ( 63 |
64 | {this.rebuiltPackageCount} of {this.incompatiblePackages.length} packages 65 | were rebuilt successfully. Reload Atom to activate them. 66 | 67 | 70 |
71 | ) 72 | } else { 73 | return ( 74 |
75 | Some installed packages could not be loaded because they contain native 76 | modules that were compiled for an earlier version of Atom. 77 | 78 | 81 |
82 | ) 83 | } 84 | } else { 85 | return ( 86 |
87 | None of your packages contain incompatible native modules. 88 |
89 | ) 90 | } 91 | } 92 | 93 | renderIncompatiblePackageList () { 94 | return ( 95 |
{ 96 | this.incompatiblePackages.map(this.renderIncompatiblePackage.bind(this)) 97 | }
98 | ) 99 | } 100 | 101 | renderIncompatiblePackage (pack) { 102 | let rebuildStatus = this.rebuildStatuses.get(pack) 103 | 104 | return ( 105 |
106 | {this.renderRebuildStatusIndicator(rebuildStatus)} 107 | 108 |

109 | {pack.name} {pack.metadata.version} 110 |

111 | { 112 | rebuildStatus 113 | ? this.renderRebuildOutput(pack) 114 | : this.renderIncompatibleModules(pack) 115 | } 116 |
117 | ) 118 | } 119 | 120 | renderRebuildStatusIndicator (rebuildStatus) { 121 | if (rebuildStatus === REBUILDING) { 122 | return ( 123 |
124 | Rebuilding 125 |
126 | ) 127 | } else if (rebuildStatus === REBUILD_SUCCEEDED) { 128 | return ( 129 |
130 | Rebuild Succeeded 131 |
132 | ) 133 | } else if (rebuildStatus === REBUILD_FAILED) { 134 | return ( 135 |
136 | Rebuild Failed 137 |
138 | ) 139 | } else { 140 | return '' 141 | } 142 | } 143 | 144 | renderRebuildOutput (pack) { 145 | if (this.rebuildStatuses.get(pack) === REBUILD_FAILED) { 146 | return
{this.rebuildFailureOutputs.get(pack)}
147 | } else { 148 | return '' 149 | } 150 | } 151 | 152 | renderIncompatibleModules (pack) { 153 | return ( 154 | 163 | ) 164 | } 165 | 166 | populateIncompatiblePackages () { 167 | this.incompatiblePackages = 168 | this.packageManager 169 | .getLoadedPackages() 170 | .filter(pack => !pack.isCompatible()) 171 | 172 | for (let pack of this.incompatiblePackages) { 173 | let buildFailureOutput = pack.getBuildFailureOutput() 174 | if (buildFailureOutput) { 175 | this.setPackageStatus(pack, REBUILD_FAILED) 176 | this.setRebuildFailureOutput(pack, buildFailureOutput) 177 | } 178 | } 179 | 180 | this.loaded = true 181 | etch.update(this) 182 | } 183 | 184 | async rebuildIncompatiblePackages () { 185 | this.rebuildInProgress = true 186 | let rebuiltPackageCount = 0 187 | for (let pack of this.incompatiblePackages) { 188 | this.setPackageStatus(pack, REBUILDING) 189 | let {code, stderr} = await pack.rebuild() 190 | if (code === 0) { 191 | this.setPackageStatus(pack, REBUILD_SUCCEEDED) 192 | rebuiltPackageCount++ 193 | } else { 194 | this.setRebuildFailureOutput(pack, stderr) 195 | this.setPackageStatus(pack, REBUILD_FAILED) 196 | } 197 | } 198 | this.rebuildInProgress = false 199 | this.rebuiltPackageCount = rebuiltPackageCount 200 | etch.update(this) 201 | } 202 | 203 | setPackageStatus (pack, status) { 204 | this.rebuildStatuses.set(pack, status) 205 | etch.update(this) 206 | } 207 | 208 | setRebuildFailureOutput (pack, output) { 209 | this.rebuildFailureOutputs.set(pack, output) 210 | etch.update(this) 211 | } 212 | 213 | getTitle () { 214 | return 'Incompatible Packages' 215 | } 216 | 217 | getURI () { 218 | return VIEW_URI 219 | } 220 | 221 | getIconName () { 222 | return 'package' 223 | } 224 | 225 | serialize () { 226 | return {deserializer: 'IncompatiblePackagesComponent'} 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /lib/main.js: -------------------------------------------------------------------------------- 1 | /** @babel */ 2 | 3 | import {Disposable, CompositeDisposable} from 'atom' 4 | import VIEW_URI from './view-uri' 5 | 6 | let disposables = null 7 | 8 | export function activate () { 9 | disposables = new CompositeDisposable() 10 | 11 | disposables.add(atom.workspace.addOpener((uri) => { 12 | if (uri === VIEW_URI) { 13 | return deserializeIncompatiblePackagesComponent() 14 | } 15 | })) 16 | 17 | disposables.add(atom.commands.add('atom-workspace', { 18 | 'incompatible-packages:view': () => { 19 | atom.workspace.open(VIEW_URI) 20 | } 21 | })) 22 | } 23 | 24 | export function deactivate () { 25 | disposables.dispose() 26 | } 27 | 28 | export function consumeStatusBar (statusBar) { 29 | let incompatibleCount = 0 30 | for (let pack of atom.packages.getLoadedPackages()) { 31 | if (!pack.isCompatible()) incompatibleCount++ 32 | } 33 | 34 | if (incompatibleCount > 0) { 35 | let icon = createIcon(incompatibleCount) 36 | let tile = statusBar.addRightTile({item: icon, priority: 200}) 37 | icon.element.addEventListener('click', () => { 38 | atom.commands.dispatch(icon.element, 'incompatible-packages:view') 39 | }) 40 | disposables.add(new Disposable(() => tile.destroy())) 41 | } 42 | } 43 | 44 | export function deserializeIncompatiblePackagesComponent () { 45 | const IncompatiblePackagesComponent = require('./incompatible-packages-component') 46 | return new IncompatiblePackagesComponent(atom.packages) 47 | } 48 | 49 | function createIcon (count) { 50 | const StatusIconComponent = require('./status-icon-component') 51 | return new StatusIconComponent({count}) 52 | } 53 | -------------------------------------------------------------------------------- /lib/status-icon-component.js: -------------------------------------------------------------------------------- 1 | /** @babel */ 2 | /** @jsx etch.dom */ 3 | 4 | import etch from 'etch' 5 | 6 | export default class StatusIconComponent { 7 | constructor ({count}) { 8 | this.count = count 9 | etch.initialize(this) 10 | } 11 | 12 | update () {} 13 | 14 | render () { 15 | return ( 16 |
17 | 18 | {this.count} 19 |
20 | ) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/view-uri.js: -------------------------------------------------------------------------------- 1 | /** @babel */ 2 | 3 | export default 'atom://incompatible-packages' 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "incompatible-packages", 3 | "main": "./lib/main", 4 | "version": "0.27.3", 5 | "description": "Show incompatible packages", 6 | "repository": "https://github.com/atom/incompatible-packages", 7 | "license": "MIT", 8 | "engines": { 9 | "atom": ">0.50.0" 10 | }, 11 | "dependencies": { 12 | "etch": "^0.12.2" 13 | }, 14 | "consumedServices": { 15 | "status-bar": { 16 | "versions": { 17 | "^1.0.0": "consumeStatusBar" 18 | } 19 | } 20 | }, 21 | "deserializers": { 22 | "IncompatiblePackagesComponent": "deserializeIncompatiblePackagesComponent" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /spec/fixtures/incompatible-package/bad.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atom/incompatible-packages/cc9465113973132ec583d96d78a937ce7c37a2e5/spec/fixtures/incompatible-package/bad.js -------------------------------------------------------------------------------- /spec/fixtures/incompatible-package/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "incompatible-package", 3 | "version": "1.0.0", 4 | "main": "./bad.js" 5 | } 6 | -------------------------------------------------------------------------------- /spec/incompatible-packages-component-spec.js: -------------------------------------------------------------------------------- 1 | /** @babel */ 2 | 3 | import etch from 'etch' 4 | import IncompatiblePackagesComponent from '../lib/incompatible-packages-component' 5 | 6 | describe('IncompatiblePackagesComponent', () => { 7 | let packages, etchScheduler 8 | 9 | beforeEach(() => { 10 | etchScheduler = etch.getScheduler() 11 | 12 | packages = [ 13 | { 14 | name: 'incompatible-1', 15 | isCompatible () { 16 | return false 17 | }, 18 | rebuild: function () { 19 | return new Promise((resolve) => this.resolveRebuild = resolve) 20 | }, 21 | getBuildFailureOutput () { 22 | return null 23 | }, 24 | path: '/Users/joe/.atom/packages/incompatible-1', 25 | metadata: { 26 | repository: 'https://github.com/atom/incompatible-1', 27 | version: '1.0.0' 28 | }, 29 | incompatibleModules: [ 30 | {name: 'x', version: '1.0.0', error: 'Expected version X, got Y'}, 31 | {name: 'y', version: '1.0.0', error: 'Expected version X, got Z'} 32 | ] 33 | }, 34 | { 35 | name: 'incompatible-2', 36 | isCompatible () { 37 | return false 38 | }, 39 | rebuild () { 40 | return new Promise((resolve) => this.resolveRebuild = resolve) 41 | }, 42 | getBuildFailureOutput () { 43 | return null 44 | }, 45 | path: '/Users/joe/.atom/packages/incompatible-2', 46 | metadata: { 47 | repository: 'https://github.com/atom/incompatible-2', 48 | version: '1.0.0' 49 | }, 50 | incompatibleModules: [ 51 | {name: 'z', version: '1.0.0', error: 'Expected version X, got Y'} 52 | ] 53 | }, 54 | { 55 | name: 'compatible', 56 | isCompatible () { 57 | return true 58 | }, 59 | rebuild () { 60 | throw new Error('Should not rebuild a compatible package') 61 | }, 62 | getBuildFailureOutput () { 63 | return null 64 | }, 65 | path: '/Users/joe/.atom/packages/b', 66 | metadata: { 67 | repository: 'https://github.com/atom/b', 68 | version: '1.0.0' 69 | }, 70 | incompatibleModules: [], 71 | } 72 | ] 73 | }) 74 | 75 | describe('when packages have not finished loading', () => { 76 | it('delays rendering incompatible packages until the end of the tick', () => { 77 | waitsForPromise(async () => { 78 | let component = 79 | new IncompatiblePackagesComponent({ 80 | getActivePackages: () => [], 81 | getLoadedPackages: () => packages 82 | }) 83 | let {element} = component 84 | 85 | expect(element.querySelectorAll('.incompatible-package').length).toEqual(0) 86 | 87 | await etchScheduler.getNextUpdatePromise() 88 | 89 | expect(element.querySelectorAll('.incompatible-package').length).toBeGreaterThan(0) 90 | }) 91 | }) 92 | }) 93 | 94 | describe('when there are no incompatible packages', () => { 95 | it('does not render incompatible packages or the rebuild button', () => { 96 | waitsForPromise(async () => { 97 | expect(packages[2].isCompatible()).toBe(true) 98 | let compatiblePackages = [packages[2]] 99 | 100 | let component = 101 | new IncompatiblePackagesComponent({ 102 | getActivePackages: () => compatiblePackages, 103 | getLoadedPackages: () => compatiblePackages 104 | }) 105 | let {element} = component 106 | 107 | await etchScheduler.getNextUpdatePromise() 108 | 109 | expect(element.querySelectorAll('.incompatible-package').length).toBe(0) 110 | expect(element.querySelector('button')).toBeNull() 111 | }) 112 | }) 113 | }) 114 | 115 | describe('when some packages previously failed to rebuild', () => { 116 | it('renders them with failed build status and error output', () => { 117 | waitsForPromise(async () => { 118 | packages[1].getBuildFailureOutput = function () { 119 | return 'The build failed' 120 | } 121 | 122 | let component = 123 | new IncompatiblePackagesComponent({ 124 | getActivePackages: () => packages, 125 | getLoadedPackages: () => packages 126 | }) 127 | let {element} = component 128 | 129 | await etchScheduler.getNextUpdatePromise() 130 | let packageElement = element.querySelector('.incompatible-package:nth-child(2)') 131 | 132 | expect(packageElement.querySelector('.badge').textContent).toBe('Rebuild Failed') 133 | expect(packageElement.querySelector('pre').textContent).toBe('The build failed') 134 | }) 135 | }) 136 | }) 137 | 138 | describe('when there are incompatible packages', () => { 139 | it('renders incompatible packages and the rebuild button', () => { 140 | waitsForPromise(async () => { 141 | let component = 142 | new IncompatiblePackagesComponent({ 143 | getActivePackages: () => packages, 144 | getLoadedPackages: () => packages 145 | }) 146 | let {element} = component 147 | 148 | await etchScheduler.getNextUpdatePromise() 149 | 150 | expect(element.querySelectorAll('.incompatible-package').length).toEqual(2) 151 | expect(element.querySelector('button')).not.toBeNull() 152 | }) 153 | }) 154 | 155 | describe('when the "Rebuild All" button is clicked', () => { 156 | it('rebuilds every incompatible package, updating each package\'s view with status', () => { 157 | waitsForPromise(async () => { 158 | let component = 159 | new IncompatiblePackagesComponent({ 160 | getActivePackages: () => packages, 161 | getLoadedPackages: () => packages 162 | }) 163 | let {element} = component 164 | jasmine.attachToDOM(element) 165 | 166 | await etchScheduler.getNextUpdatePromise() 167 | 168 | component.refs.rebuildButton.dispatchEvent(new CustomEvent('click', {bubbles: true})) 169 | await etchScheduler.getNextUpdatePromise() // view update 170 | 171 | expect(component.refs.rebuildButton.disabled).toBe(true) 172 | 173 | expect(packages[0].resolveRebuild).toBeDefined() 174 | 175 | expect(element.querySelector('.incompatible-package:nth-child(1) .badge').textContent).toBe('Rebuilding') 176 | expect(element.querySelector('.incompatible-package:nth-child(2) .badge')).toBeNull() 177 | 178 | packages[0].resolveRebuild({code: 0}) // simulate rebuild success 179 | await etchScheduler.getNextUpdatePromise() // view update 180 | 181 | expect(packages[1].resolveRebuild).toBeDefined() 182 | 183 | expect(element.querySelector('.incompatible-package:nth-child(1) .badge').textContent).toBe('Rebuild Succeeded') 184 | expect(element.querySelector('.incompatible-package:nth-child(2) .badge').textContent).toBe('Rebuilding') 185 | 186 | packages[1].resolveRebuild({code: 12, stderr: 'This is an error from the test!'}) // simulate rebuild failure 187 | await etchScheduler.getNextUpdatePromise() // view update 188 | 189 | expect(element.querySelector('.incompatible-package:nth-child(1) .badge').textContent).toBe('Rebuild Succeeded') 190 | expect(element.querySelector('.incompatible-package:nth-child(2) .badge').textContent).toBe('Rebuild Failed') 191 | expect(element.querySelector('.incompatible-package:nth-child(2) pre').textContent).toBe('This is an error from the test!') 192 | }) 193 | }) 194 | 195 | it('displays a prompt to reload Atom when the packages finish rebuilding', () => { 196 | waitsForPromise(async () => { 197 | let component = 198 | new IncompatiblePackagesComponent({ 199 | getActivePackages: () => packages, 200 | getLoadedPackages: () => packages 201 | }) 202 | let {element} = component 203 | jasmine.attachToDOM(element) 204 | await etchScheduler.getNextUpdatePromise() // view update 205 | 206 | component.refs.rebuildButton.dispatchEvent(new CustomEvent('click', {bubbles: true})) 207 | expect(packages[0].resolveRebuild({code: 0})) 208 | await new Promise(global.setImmediate) 209 | expect(packages[1].resolveRebuild({code: 0})) 210 | 211 | await etchScheduler.getNextUpdatePromise() // view update 212 | 213 | expect(component.refs.reloadButton).toBeDefined() 214 | expect(element.querySelector('.alert').textContent).toMatch(/2 of 2/) 215 | 216 | spyOn(atom, 'reload') 217 | component.refs.reloadButton.dispatchEvent(new CustomEvent('click', {bubbles: true})) 218 | expect(atom.reload).toHaveBeenCalled() 219 | }) 220 | }) 221 | }) 222 | 223 | describe('when the "Package Settings" button is clicked', () => { 224 | it('opens the settings panel for the package', () => { 225 | waitsForPromise(async () => { 226 | let component = 227 | new IncompatiblePackagesComponent({ 228 | getActivePackages: () => packages, 229 | getLoadedPackages: () => packages 230 | }) 231 | let {element} = component 232 | jasmine.attachToDOM(element) 233 | 234 | await etchScheduler.getNextUpdatePromise() 235 | 236 | spyOn(atom.workspace, 'open') 237 | element.querySelector('.incompatible-package:nth-child(2) button').dispatchEvent(new CustomEvent('click', {bubbles: true})) 238 | expect(atom.workspace.open).toHaveBeenCalledWith('atom://config/packages/incompatible-2') 239 | }) 240 | }) 241 | }) 242 | }) 243 | }) 244 | -------------------------------------------------------------------------------- /spec/incompatible-packages-spec.js: -------------------------------------------------------------------------------- 1 | /** @babel */ 2 | 3 | import path from 'path' 4 | import IncompatiblePackagesComponent from '../lib/incompatible-packages-component' 5 | import StatusIconComponent from '../lib/status-icon-component' 6 | 7 | // This exists only so that CI passes on both Atom 1.6 and Atom 1.8+. 8 | function findStatusBar () { 9 | if (typeof atom.workspace.getFooterPanels === 'function') { 10 | const footerPanels = atom.workspace.getFooterPanels() 11 | if (footerPanels.length > 0) { 12 | return footerPanels[0].getItem() 13 | } 14 | } 15 | 16 | return atom.workspace.getBottomPanels()[0].getItem() 17 | } 18 | 19 | describe('Incompatible packages', () => { 20 | let statusBar 21 | 22 | beforeEach(() => { 23 | atom.views.getView(atom.workspace) 24 | 25 | waitsForPromise(() => atom.packages.activatePackage('status-bar')) 26 | 27 | runs(() => { 28 | statusBar = findStatusBar() 29 | }) 30 | }) 31 | 32 | describe('when there are packages with incompatible native modules', () => { 33 | beforeEach(() => { 34 | let incompatiblePackage = atom.packages.loadPackage( 35 | path.join(__dirname, 'fixtures', 'incompatible-package') 36 | ) 37 | spyOn(incompatiblePackage, 'isCompatible').andReturn(false) 38 | incompatiblePackage.incompatibleModules = [] 39 | waitsForPromise(() => atom.packages.activatePackage("incompatible-packages")) 40 | 41 | waits(1) 42 | }) 43 | 44 | it('adds an icon to the status bar', () => { 45 | let statusBarIcon = statusBar.getRightTiles()[0].getItem() 46 | expect(statusBarIcon.constructor).toBe(StatusIconComponent) 47 | }) 48 | 49 | describe('clicking the icon', () => { 50 | it('displays the incompatible packages view in a pane', () => { 51 | let statusBarIcon = statusBar.getRightTiles()[0].getItem() 52 | statusBarIcon.element.dispatchEvent(new MouseEvent('click')) 53 | 54 | let activePaneItem 55 | waitsFor(() => (activePaneItem = atom.workspace.getActivePaneItem())) 56 | 57 | runs(() => { 58 | expect(activePaneItem.constructor).toBe(IncompatiblePackagesComponent) 59 | }) 60 | }) 61 | }) 62 | }) 63 | 64 | describe('when there are no packages with incompatible native modules', () => { 65 | beforeEach(() => { 66 | waitsForPromise(() => atom.packages.activatePackage("incompatible-packages")) 67 | }) 68 | 69 | it('does not add an icon to the status bar', () => { 70 | let statusBarItemClasses = statusBar 71 | .getRightTiles() 72 | .map((tile) => tile.getItem().className) 73 | 74 | expect(statusBarItemClasses).not.toContain('incompatible-packages') 75 | }) 76 | }) 77 | }) 78 | -------------------------------------------------------------------------------- /styles/incompatible-packages.less: -------------------------------------------------------------------------------- 1 | @import "ui-variables"; 2 | 3 | .incompatible-packages { 4 | background-color: @pane-item-background-color; 5 | overflow-y: scroll; 6 | 7 | .incompatible-package { 8 | padding: 15px; 9 | margin-bottom: 10px; 10 | border-radius: 6px; 11 | border: 1px solid #d1d1d2; 12 | background-color: #fafafa; 13 | overflow: hidden; 14 | 15 | .badge { 16 | margin-left: 1em; 17 | } 18 | 19 | .heading { 20 | margin-top: 0px; 21 | } 22 | 23 | ul { 24 | padding-left: 1em; 25 | } 26 | 27 | li { 28 | list-style-type: none; 29 | } 30 | 31 | pre { 32 | margin-top: 2em; 33 | max-height: 25em; 34 | overflow: scroll; 35 | color: @text-color-error; 36 | } 37 | } 38 | } 39 | 40 | .incompatible-packages-status { 41 | padding-left: 2px; 42 | } 43 | --------------------------------------------------------------------------------