12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898 |
- const path = require('path');
- const url = require('url');
- const Package = require('../src/package');
- const PackageManager = require('../src/package-manager');
- const temp = require('temp').track();
- const fs = require('fs-plus');
- const { Disposable } = require('atom');
- const { buildKeydownEvent } = require('../src/keymap-extensions');
- const { mockLocalStorage } = require('./spec-helper');
- const ModuleCache = require('../src/module-cache');
- describe('PackageManager', () => {
- function createTestElement(className) {
- const element = document.createElement('div');
- element.className = className;
- return element;
- }
- beforeEach(() => {
- spyOn(ModuleCache, 'add');
- });
- describe('initialize', () => {
- it('adds regular package path', () => {
- const packageManger = new PackageManager({});
- const configDirPath = path.join('~', 'someConfig');
- packageManger.initialize({ configDirPath });
- expect(packageManger.packageDirPaths.length).toBe(1);
- expect(packageManger.packageDirPaths[0]).toBe(
- path.join(configDirPath, 'packages')
- );
- });
- it('adds regular package path, dev package path, and Atom repo package path in dev mode and dev resource path is set', () => {
- const packageManger = new PackageManager({});
- const configDirPath = path.join('~', 'someConfig');
- const resourcePath = path.join('~', '/atom');
- packageManger.initialize({ configDirPath, resourcePath, devMode: true });
- expect(packageManger.packageDirPaths.length).toBe(3);
- expect(packageManger.packageDirPaths).toContain(
- path.join(configDirPath, 'packages')
- );
- expect(packageManger.packageDirPaths).toContain(
- path.join(configDirPath, 'dev', 'packages')
- );
- expect(packageManger.packageDirPaths).toContain(
- path.join(resourcePath, 'packages')
- );
- });
- });
- describe('::getApmPath()', () => {
- /**
- * TODO: FAILING TEST - This test fails with the following output:
- * Expected '/home/runner/work/pulsar/pulsar/apm/node_modules/ppm/bin/apm' to be
- * '/home/runner/work/pulsar/pulsar/node_modules/electron/dist/resources/app/apm/bin/apm'
- */
- xit('returns the path to the apm command', () => {
- let apmPath = path.join(
- process.resourcesPath,
- 'app',
- 'apm',
- 'bin',
- 'apm'
- );
- if (process.platform === 'win32') {
- apmPath += '.cmd';
- }
- expect(atom.packages.getApmPath()).toBe(apmPath);
- });
- describe('when the core.apmPath setting is set', () => {
- beforeEach(() => atom.config.set('core.apmPath', '/path/to/apm'));
- it('returns the value of the core.apmPath config setting', () => {
- expect(atom.packages.getApmPath()).toBe('/path/to/apm');
- });
- });
- });
- describe('::loadPackages()', () => {
- beforeEach(() => spyOn(atom.packages, 'loadAvailablePackage'));
- afterEach(async () => {
- await atom.packages.deactivatePackages();
- atom.packages.unloadPackages();
- });
- it('sets hasLoadedInitialPackages', () => {
- expect(atom.packages.hasLoadedInitialPackages()).toBe(false);
- atom.packages.loadPackages();
- expect(atom.packages.hasLoadedInitialPackages()).toBe(true);
- });
- });
- describe('::loadPackage(name)', () => {
- beforeEach(() => atom.config.set('core.disabledPackages', []));
- it('returns the package', () => {
- const pack = atom.packages.loadPackage('package-with-index');
- expect(pack instanceof Package).toBe(true);
- expect(pack.metadata.name).toBe('package-with-index');
- });
- it('returns the package if it has an invalid keymap', () => {
- spyOn(atom, 'inSpecMode').andReturn(false);
- const pack = atom.packages.loadPackage('package-with-broken-keymap');
- expect(pack instanceof Package).toBe(true);
- expect(pack.metadata.name).toBe('package-with-broken-keymap');
- });
- it('returns the package if it has an invalid stylesheet', () => {
- spyOn(atom, 'inSpecMode').andReturn(false);
- const pack = atom.packages.loadPackage('package-with-invalid-styles');
- expect(pack instanceof Package).toBe(true);
- expect(pack.metadata.name).toBe('package-with-invalid-styles');
- expect(pack.stylesheets.length).toBe(0);
- const addErrorHandler = jasmine.createSpy();
- atom.notifications.onDidAddNotification(addErrorHandler);
- expect(() => pack.reloadStylesheets()).not.toThrow();
- expect(addErrorHandler.callCount).toBe(2);
- expect(addErrorHandler.argsForCall[1][0].message).toContain(
- 'Failed to reload the package-with-invalid-styles package stylesheets'
- );
- expect(addErrorHandler.argsForCall[1][0].options.packageName).toEqual(
- 'package-with-invalid-styles'
- );
- });
- it('returns null if the package has an invalid package.json', () => {
- spyOn(atom, 'inSpecMode').andReturn(false);
- const addErrorHandler = jasmine.createSpy();
- atom.notifications.onDidAddNotification(addErrorHandler);
- expect(
- atom.packages.loadPackage('package-with-broken-package-json')
- ).toBeNull();
- expect(addErrorHandler.callCount).toBe(1);
- expect(addErrorHandler.argsForCall[0][0].message).toContain(
- 'Failed to load the package-with-broken-package-json package'
- );
- expect(addErrorHandler.argsForCall[0][0].options.packageName).toEqual(
- 'package-with-broken-package-json'
- );
- });
- it('returns null if the package name or path starts with a dot', () => {
- expect(
- atom.packages.loadPackage('/Users/user/.atom/packages/.git')
- ).toBeNull();
- });
- it('normalizes short repository urls in package.json', () => {
- let { metadata } = atom.packages.loadPackage(
- 'package-with-short-url-package-json'
- );
- expect(metadata.repository.type).toBe('git');
- expect(metadata.repository.url).toBe('https://github.com/example/repo');
- ({ metadata } = atom.packages.loadPackage(
- 'package-with-invalid-url-package-json'
- ));
- expect(metadata.repository.type).toBe('git');
- expect(metadata.repository.url).toBe('foo');
- });
- it('trims git+ from the beginning and .git from the end of repository URLs, even if npm already normalized them ', () => {
- const { metadata } = atom.packages.loadPackage(
- 'package-with-prefixed-and-suffixed-repo-url'
- );
- expect(metadata.repository.type).toBe('git');
- expect(metadata.repository.url).toBe('https://github.com/example/repo');
- });
- it('returns null if the package is not found in any package directory', () => {
- spyOn(console, 'warn');
- expect(
- atom.packages.loadPackage('this-package-cannot-be-found')
- ).toBeNull();
- expect(console.warn.callCount).toBe(1);
- expect(console.warn.argsForCall[0][0]).toContain('Could not resolve');
- });
- it('invokes ::onDidLoadPackage listeners with the loaded package', () => {
- let loadedPackage = null;
- atom.packages.onDidLoadPackage(pack => {
- loadedPackage = pack;
- });
- atom.packages.loadPackage('package-with-main');
- expect(loadedPackage.name).toBe('package-with-main');
- });
- it("registers any deserializers specified in the package's package.json", () => {
- atom.packages.loadPackage('package-with-deserializers');
- const state1 = { deserializer: 'Deserializer1', a: 'b' };
- expect(atom.deserializers.deserialize(state1)).toEqual({
- wasDeserializedBy: 'deserializeMethod1',
- state: state1
- });
- const state2 = { deserializer: 'Deserializer2', c: 'd' };
- expect(atom.deserializers.deserialize(state2)).toEqual({
- wasDeserializedBy: 'deserializeMethod2',
- state: state2
- });
- });
- it('early-activates any atom.directory-provider or atom.repository-provider services that the package provide', () => {
- jasmine.useRealClock();
- const providers = [];
- atom.packages.serviceHub.consume(
- 'atom.directory-provider',
- '^0.1.0',
- provider => providers.push(provider)
- );
- atom.packages.loadPackage('package-with-directory-provider');
- expect(providers.map(p => p.name)).toEqual([
- 'directory provider from package-with-directory-provider'
- ]);
- });
- describe("when there are view providers specified in the package's package.json", () => {
- const model1 = { worksWithViewProvider1: true };
- const model2 = { worksWithViewProvider2: true };
- afterEach(async () => {
- await atom.packages.deactivatePackage('package-with-view-providers');
- atom.packages.unloadPackage('package-with-view-providers');
- });
- it('does not load the view providers immediately', () => {
- const pack = atom.packages.loadPackage('package-with-view-providers');
- expect(pack.mainModule).toBeNull();
- expect(() => atom.views.getView(model1)).toThrow();
- expect(() => atom.views.getView(model2)).toThrow();
- });
- it('registers the view providers when the package is activated', async () => {
- atom.packages.loadPackage('package-with-view-providers');
- await atom.packages.activatePackage('package-with-view-providers');
- const element1 = atom.views.getView(model1);
- expect(element1 instanceof HTMLDivElement).toBe(true);
- expect(element1.dataset.createdBy).toBe('view-provider-1');
- const element2 = atom.views.getView(model2);
- expect(element2 instanceof HTMLDivElement).toBe(true);
- expect(element2.dataset.createdBy).toBe('view-provider-2');
- });
- it("registers the view providers when any of the package's deserializers are used", () => {
- atom.packages.loadPackage('package-with-view-providers');
- spyOn(atom.views, 'addViewProvider').andCallThrough();
- atom.deserializers.deserialize({
- deserializer: 'DeserializerFromPackageWithViewProviders',
- a: 'b'
- });
- expect(atom.views.addViewProvider.callCount).toBe(2);
- atom.deserializers.deserialize({
- deserializer: 'DeserializerFromPackageWithViewProviders',
- a: 'b'
- });
- expect(atom.views.addViewProvider.callCount).toBe(2);
- const element1 = atom.views.getView(model1);
- expect(element1 instanceof HTMLDivElement).toBe(true);
- expect(element1.dataset.createdBy).toBe('view-provider-1');
- const element2 = atom.views.getView(model2);
- expect(element2 instanceof HTMLDivElement).toBe(true);
- expect(element2.dataset.createdBy).toBe('view-provider-2');
- });
- });
- it("registers the config schema in the package's metadata, if present", () => {
- let pack = atom.packages.loadPackage('package-with-json-config-schema');
- expect(atom.config.getSchema('package-with-json-config-schema')).toEqual({
- type: 'object',
- properties: {
- a: { type: 'number', default: 5 },
- b: { type: 'string', default: 'five' }
- }
- });
- expect(pack.mainModule).toBeNull();
- atom.packages.unloadPackage('package-with-json-config-schema');
- atom.config.clear();
- pack = atom.packages.loadPackage('package-with-json-config-schema');
- expect(atom.config.getSchema('package-with-json-config-schema')).toEqual({
- type: 'object',
- properties: {
- a: { type: 'number', default: 5 },
- b: { type: 'string', default: 'five' }
- }
- });
- });
- describe('when a package does not have deserializers, view providers or a config schema in its package.json', () => {
- beforeEach(() => mockLocalStorage());
- it("defers loading the package's main module if the package previously used no Atom APIs when its main module was required", () => {
- const pack1 = atom.packages.loadPackage('package-with-main');
- expect(pack1.mainModule).toBeDefined();
- atom.packages.unloadPackage('package-with-main');
- const pack2 = atom.packages.loadPackage('package-with-main');
- expect(pack2.mainModule).toBeNull();
- });
- it("does not defer loading the package's main module if the package previously used Atom APIs when its main module was required", () => {
- const pack1 = atom.packages.loadPackage(
- 'package-with-eval-time-api-calls'
- );
- expect(pack1.mainModule).toBeDefined();
- atom.packages.unloadPackage('package-with-eval-time-api-calls');
- const pack2 = atom.packages.loadPackage(
- 'package-with-eval-time-api-calls'
- );
- expect(pack2.mainModule).not.toBeNull();
- });
- });
- });
- describe('::loadAvailablePackage(availablePackage)', () => {
- describe('if the package was preloaded', () => {
- it('adds the package path to the module cache', () => {
- const availablePackage = atom.packages
- .getAvailablePackages()
- .find(p => p.name === 'spell-check');
- availablePackage.isBundled = true;
- expect(
- atom.packages.preloadedPackages[availablePackage.name]
- ).toBeUndefined();
- expect(atom.packages.isPackageLoaded(availablePackage.name)).toBe(
- false
- );
- const metadata = atom.packages.loadPackageMetadata(availablePackage);
- atom.packages.preloadPackage(availablePackage.name, {
- rootDirPath: path.relative(
- atom.packages.resourcePath,
- availablePackage.path
- ),
- metadata
- });
- atom.packages.loadAvailablePackage(availablePackage);
- expect(atom.packages.isPackageLoaded(availablePackage.name)).toBe(true);
- expect(ModuleCache.add).toHaveBeenCalledWith(
- availablePackage.path,
- metadata
- );
- });
- it('deactivates it if it had been disabled', () => {
- const availablePackage = atom.packages
- .getAvailablePackages()
- .find(p => p.name === 'spell-check');
- availablePackage.isBundled = true;
- expect(
- atom.packages.preloadedPackages[availablePackage.name]
- ).toBeUndefined();
- expect(atom.packages.isPackageLoaded(availablePackage.name)).toBe(
- false
- );
- const metadata = atom.packages.loadPackageMetadata(availablePackage);
- const preloadedPackage = atom.packages.preloadPackage(
- availablePackage.name,
- {
- rootDirPath: path.relative(
- atom.packages.resourcePath,
- availablePackage.path
- ),
- metadata
- }
- );
- expect(preloadedPackage.keymapActivated).toBe(true);
- expect(preloadedPackage.settingsActivated).toBe(true);
- expect(preloadedPackage.menusActivated).toBe(true);
- atom.packages.loadAvailablePackage(
- availablePackage,
- new Set([availablePackage.name])
- );
- expect(atom.packages.isPackageLoaded(availablePackage.name)).toBe(
- false
- );
- expect(preloadedPackage.keymapActivated).toBe(false);
- expect(preloadedPackage.settingsActivated).toBe(false);
- expect(preloadedPackage.menusActivated).toBe(false);
- });
- it('deactivates it and reloads the new one if trying to load the same package outside of the bundle', () => {
- const availablePackage = atom.packages
- .getAvailablePackages()
- .find(p => p.name === 'spell-check');
- availablePackage.isBundled = true;
- expect(
- atom.packages.preloadedPackages[availablePackage.name]
- ).toBeUndefined();
- expect(atom.packages.isPackageLoaded(availablePackage.name)).toBe(
- false
- );
- const metadata = atom.packages.loadPackageMetadata(availablePackage);
- const preloadedPackage = atom.packages.preloadPackage(
- availablePackage.name,
- {
- rootDirPath: path.relative(
- atom.packages.resourcePath,
- availablePackage.path
- ),
- metadata
- }
- );
- expect(preloadedPackage.keymapActivated).toBe(true);
- expect(preloadedPackage.settingsActivated).toBe(true);
- expect(preloadedPackage.menusActivated).toBe(true);
- availablePackage.isBundled = false;
- atom.packages.loadAvailablePackage(availablePackage);
- expect(atom.packages.isPackageLoaded(availablePackage.name)).toBe(true);
- expect(preloadedPackage.keymapActivated).toBe(false);
- expect(preloadedPackage.settingsActivated).toBe(false);
- expect(preloadedPackage.menusActivated).toBe(false);
- });
- });
- describe('if the package was not preloaded', () => {
- it('adds the package path to the module cache', () => {
- const availablePackage = atom.packages
- .getAvailablePackages()
- .find(p => p.name === 'spell-check');
- availablePackage.isBundled = true;
- const metadata = atom.packages.loadPackageMetadata(availablePackage);
- atom.packages.loadAvailablePackage(availablePackage);
- expect(ModuleCache.add).toHaveBeenCalledWith(
- availablePackage.path,
- metadata
- );
- });
- });
- });
- describe('preloading', () => {
- it('requires the main module, loads the config schema and activates keymaps, menus and settings without reactivating them during package activation', () => {
- const availablePackage = atom.packages
- .getAvailablePackages()
- .find(p => p.name === 'spell-check');
- availablePackage.isBundled = true;
- const metadata = atom.packages.loadPackageMetadata(availablePackage);
- expect(
- atom.packages.preloadedPackages[availablePackage.name]
- ).toBeUndefined();
- expect(atom.packages.isPackageLoaded(availablePackage.name)).toBe(false);
- atom.packages.packagesCache = {};
- atom.packages.packagesCache[availablePackage.name] = {
- main: path.join(availablePackage.path, metadata.main),
- grammarPaths: []
- };
- const preloadedPackage = atom.packages.preloadPackage(
- availablePackage.name,
- {
- rootDirPath: path.relative(
- atom.packages.resourcePath,
- availablePackage.path
- ),
- metadata
- }
- );
- expect(preloadedPackage.keymapActivated).toBe(true);
- expect(preloadedPackage.settingsActivated).toBe(true);
- expect(preloadedPackage.menusActivated).toBe(true);
- expect(preloadedPackage.mainModule).toBeTruthy();
- expect(preloadedPackage.configSchemaRegisteredOnLoad).toBeTruthy();
- spyOn(atom.keymaps, 'add');
- spyOn(atom.menu, 'add');
- spyOn(atom.contextMenu, 'add');
- spyOn(atom.config, 'setSchema');
- atom.packages.loadAvailablePackage(availablePackage);
- expect(preloadedPackage.getMainModulePath()).toBe(
- path.join(availablePackage.path, metadata.main)
- );
- atom.packages.activatePackage(availablePackage.name);
- expect(atom.keymaps.add).not.toHaveBeenCalled();
- expect(atom.menu.add).not.toHaveBeenCalled();
- expect(atom.contextMenu.add).not.toHaveBeenCalled();
- expect(atom.config.setSchema).not.toHaveBeenCalled();
- expect(preloadedPackage.keymapActivated).toBe(true);
- expect(preloadedPackage.settingsActivated).toBe(true);
- expect(preloadedPackage.menusActivated).toBe(true);
- expect(preloadedPackage.mainModule).toBeTruthy();
- expect(preloadedPackage.configSchemaRegisteredOnLoad).toBeTruthy();
- });
- it('deactivates disabled keymaps during package activation', () => {
- const availablePackage = atom.packages
- .getAvailablePackages()
- .find(p => p.name === 'spell-check');
- availablePackage.isBundled = true;
- const metadata = atom.packages.loadPackageMetadata(availablePackage);
- expect(
- atom.packages.preloadedPackages[availablePackage.name]
- ).toBeUndefined();
- expect(atom.packages.isPackageLoaded(availablePackage.name)).toBe(false);
- atom.packages.packagesCache = {};
- atom.packages.packagesCache[availablePackage.name] = {
- main: path.join(availablePackage.path, metadata.main),
- grammarPaths: []
- };
- const preloadedPackage = atom.packages.preloadPackage(
- availablePackage.name,
- {
- rootDirPath: path.relative(
- atom.packages.resourcePath,
- availablePackage.path
- ),
- metadata
- }
- );
- expect(preloadedPackage.keymapActivated).toBe(true);
- expect(preloadedPackage.settingsActivated).toBe(true);
- expect(preloadedPackage.menusActivated).toBe(true);
- atom.packages.loadAvailablePackage(availablePackage);
- atom.config.set('core.packagesWithKeymapsDisabled', [
- availablePackage.name
- ]);
- atom.packages.activatePackage(availablePackage.name);
- expect(preloadedPackage.keymapActivated).toBe(false);
- expect(preloadedPackage.settingsActivated).toBe(true);
- expect(preloadedPackage.menusActivated).toBe(true);
- });
- });
- describe('::unloadPackage(name)', () => {
- describe('when the package is active', () => {
- it('throws an error', async () => {
- const pack = await atom.packages.activatePackage('package-with-main');
- expect(atom.packages.isPackageLoaded(pack.name)).toBeTruthy();
- expect(atom.packages.isPackageActive(pack.name)).toBeTruthy();
- expect(() => atom.packages.unloadPackage(pack.name)).toThrow();
- expect(atom.packages.isPackageLoaded(pack.name)).toBeTruthy();
- expect(atom.packages.isPackageActive(pack.name)).toBeTruthy();
- });
- });
- describe('when the package is not loaded', () => {
- it('throws an error', () => {
- expect(atom.packages.isPackageLoaded('unloaded')).toBeFalsy();
- expect(() => atom.packages.unloadPackage('unloaded')).toThrow();
- expect(atom.packages.isPackageLoaded('unloaded')).toBeFalsy();
- });
- });
- describe('when the package is loaded', () => {
- it('no longers reports it as being loaded', () => {
- const pack = atom.packages.loadPackage('package-with-main');
- expect(atom.packages.isPackageLoaded(pack.name)).toBeTruthy();
- atom.packages.unloadPackage(pack.name);
- expect(atom.packages.isPackageLoaded(pack.name)).toBeFalsy();
- });
- });
- it('invokes ::onDidUnloadPackage listeners with the unloaded package', () => {
- atom.packages.loadPackage('package-with-main');
- let unloadedPackage;
- atom.packages.onDidUnloadPackage(pack => {
- unloadedPackage = pack;
- });
- atom.packages.unloadPackage('package-with-main');
- expect(unloadedPackage.name).toBe('package-with-main');
- });
- });
- describe('::activatePackage(id)', () => {
- describe('when called multiple times', () => {
- it('it only calls activate on the package once', async () => {
- spyOn(Package.prototype, 'activateNow').andCallThrough();
- await atom.packages.activatePackage('package-with-index');
- await atom.packages.activatePackage('package-with-index');
- await atom.packages.activatePackage('package-with-index');
- expect(Package.prototype.activateNow.callCount).toBe(1);
- });
- });
- describe('when the package has a main module', () => {
- beforeEach(() => {
- spyOn(Package.prototype, 'requireMainModule').andCallThrough();
- });
- describe('when the metadata specifies a main module path˜', () => {
- it('requires the module at the specified path', async () => {
- const mainModule = require('./fixtures/packages/package-with-main/main-module');
- spyOn(mainModule, 'activate');
- const pack = await atom.packages.activatePackage('package-with-main');
- expect(mainModule.activate).toHaveBeenCalled();
- expect(pack.mainModule).toBe(mainModule);
- });
- });
- describe('when the metadata does not specify a main module', () => {
- it('requires index.coffee', async () => {
- const indexModule = require('./fixtures/packages/package-with-index/index');
- spyOn(indexModule, 'activate');
- const pack = await atom.packages.activatePackage(
- 'package-with-index'
- );
- expect(indexModule.activate).toHaveBeenCalled();
- expect(pack.mainModule).toBe(indexModule);
- });
- });
- it('assigns config schema, including defaults when package contains a schema', async () => {
- expect(
- atom.config.get('package-with-config-schema.numbers.one')
- ).toBeUndefined();
- await atom.packages.activatePackage('package-with-config-schema');
- expect(atom.config.get('package-with-config-schema.numbers.one')).toBe(
- 1
- );
- expect(atom.config.get('package-with-config-schema.numbers.two')).toBe(
- 2
- );
- expect(
- atom.config.set('package-with-config-schema.numbers.one', 'nope')
- ).toBe(false);
- expect(
- atom.config.set('package-with-config-schema.numbers.one', '10')
- ).toBe(true);
- expect(atom.config.get('package-with-config-schema.numbers.one')).toBe(
- 10
- );
- });
- describe('when the package metadata includes `activationCommands`', () => {
- let mainModule, promise, workspaceCommandListener, registration;
- beforeEach(() => {
- jasmine.attachToDOM(atom.workspace.getElement());
- mainModule = require('./fixtures/packages/package-with-activation-commands/index');
- mainModule.activationCommandCallCount = 0;
- spyOn(mainModule, 'activate').andCallThrough();
- workspaceCommandListener = jasmine.createSpy(
- 'workspaceCommandListener'
- );
- registration = atom.commands.add(
- 'atom-workspace',
- 'activation-command',
- workspaceCommandListener
- );
- promise = atom.packages.activatePackage(
- 'package-with-activation-commands'
- );
- });
- afterEach(() => {
- if (registration) {
- registration.dispose();
- }
- mainModule = null;
- });
- it('defers requiring/activating the main module until an activation event bubbles to the root view', async () => {
- expect(Package.prototype.requireMainModule.callCount).toBe(0);
- atom.workspace
- .getElement()
- .dispatchEvent(
- new CustomEvent('activation-command', { bubbles: true })
- );
- await promise;
- expect(Package.prototype.requireMainModule.callCount).toBe(1);
- });
- it('triggers the activation event on all handlers registered during activation', async () => {
- await atom.workspace.open();
- const editorElement = atom.workspace
- .getActiveTextEditor()
- .getElement();
- const editorCommandListener = jasmine.createSpy(
- 'editorCommandListener'
- );
- atom.commands.add(
- 'atom-text-editor',
- 'activation-command',
- editorCommandListener
- );
- atom.commands.dispatch(editorElement, 'activation-command');
- expect(mainModule.activate.callCount).toBe(1);
- expect(mainModule.activationCommandCallCount).toBe(1);
- expect(editorCommandListener.callCount).toBe(1);
- expect(workspaceCommandListener.callCount).toBe(1);
- atom.commands.dispatch(editorElement, 'activation-command');
- expect(mainModule.activationCommandCallCount).toBe(2);
- expect(editorCommandListener.callCount).toBe(2);
- expect(workspaceCommandListener.callCount).toBe(2);
- expect(mainModule.activate.callCount).toBe(1);
- });
- it('activates the package immediately when the events are empty', async () => {
- mainModule = require('./fixtures/packages/package-with-empty-activation-commands/index');
- spyOn(mainModule, 'activate').andCallThrough();
- atom.packages.activatePackage(
- 'package-with-empty-activation-commands'
- );
- expect(mainModule.activate.callCount).toBe(1);
- });
- it('adds a notification when the activation commands are invalid', () => {
- spyOn(atom, 'inSpecMode').andReturn(false);
- const addErrorHandler = jasmine.createSpy();
- atom.notifications.onDidAddNotification(addErrorHandler);
- expect(() =>
- atom.packages.activatePackage(
- 'package-with-invalid-activation-commands'
- )
- ).not.toThrow();
- expect(addErrorHandler.callCount).toBe(1);
- expect(addErrorHandler.argsForCall[0][0].message).toContain(
- 'Failed to activate the package-with-invalid-activation-commands package'
- );
- expect(addErrorHandler.argsForCall[0][0].options.packageName).toEqual(
- 'package-with-invalid-activation-commands'
- );
- });
- it('adds a notification when the context menu is invalid', () => {
- spyOn(atom, 'inSpecMode').andReturn(false);
- const addErrorHandler = jasmine.createSpy();
- atom.notifications.onDidAddNotification(addErrorHandler);
- expect(() =>
- atom.packages.activatePackage('package-with-invalid-context-menu')
- ).not.toThrow();
- expect(addErrorHandler.callCount).toBe(1);
- expect(addErrorHandler.argsForCall[0][0].message).toContain(
- 'Failed to activate the package-with-invalid-context-menu package'
- );
- expect(addErrorHandler.argsForCall[0][0].options.packageName).toEqual(
- 'package-with-invalid-context-menu'
- );
- });
- it('adds a notification when the grammar is invalid', async () => {
- let notificationEvent;
- await new Promise(resolve => {
- const subscription = atom.notifications.onDidAddNotification(
- event => {
- notificationEvent = event;
- subscription.dispose();
- resolve();
- }
- );
- atom.packages.activatePackage('package-with-invalid-grammar');
- });
- expect(notificationEvent.message).toContain(
- 'Failed to load a package-with-invalid-grammar package grammar'
- );
- expect(notificationEvent.options.packageName).toEqual(
- 'package-with-invalid-grammar'
- );
- });
- it('adds a notification when the settings are invalid', async () => {
- let notificationEvent;
- await new Promise(resolve => {
- const subscription = atom.notifications.onDidAddNotification(
- event => {
- notificationEvent = event;
- subscription.dispose();
- resolve();
- }
- );
- atom.packages.activatePackage('package-with-invalid-settings');
- });
- expect(notificationEvent.message).toContain(
- 'Failed to load the package-with-invalid-settings package settings'
- );
- expect(notificationEvent.options.packageName).toEqual(
- 'package-with-invalid-settings'
- );
- });
- });
- describe('when the package metadata includes both activation commands and deserializers', () => {
- let mainModule, promise, workspaceCommandListener, registration;
- beforeEach(() => {
- jasmine.attachToDOM(atom.workspace.getElement());
- spyOn(atom.packages, 'hasActivatedInitialPackages').andReturn(true);
- mainModule = require('./fixtures/packages/package-with-activation-commands-and-deserializers/index');
- mainModule.activationCommandCallCount = 0;
- spyOn(mainModule, 'activate').andCallThrough();
- workspaceCommandListener = jasmine.createSpy(
- 'workspaceCommandListener'
- );
- registration = atom.commands.add(
- '.workspace',
- 'activation-command-2',
- workspaceCommandListener
- );
- promise = atom.packages.activatePackage(
- 'package-with-activation-commands-and-deserializers'
- );
- });
- afterEach(() => {
- if (registration) {
- registration.dispose();
- }
- mainModule = null;
- });
- it('activates the package when a deserializer is called', async () => {
- expect(Package.prototype.requireMainModule.callCount).toBe(0);
- const state1 = { deserializer: 'Deserializer1', a: 'b' };
- expect(atom.deserializers.deserialize(state1, atom)).toEqual({
- wasDeserializedBy: 'deserializeMethod1',
- state: state1
- });
- await promise;
- expect(Package.prototype.requireMainModule.callCount).toBe(1);
- });
- it('defers requiring/activating the main module until an activation event bubbles to the root view', async () => {
- expect(Package.prototype.requireMainModule.callCount).toBe(0);
- atom.workspace
- .getElement()
- .dispatchEvent(
- new CustomEvent('activation-command-2', { bubbles: true })
- );
- await promise;
- expect(mainModule.activate.callCount).toBe(1);
- expect(mainModule.activationCommandCallCount).toBe(1);
- expect(Package.prototype.requireMainModule.callCount).toBe(1);
- });
- });
- describe('when the package metadata includes `activationHooks`', () => {
- let mainModule, promise;
- beforeEach(() => {
- mainModule = require('./fixtures/packages/package-with-activation-hooks/index');
- spyOn(mainModule, 'activate').andCallThrough();
- });
- it('defers requiring/activating the main module until an triggering of an activation hook occurs', async () => {
- promise = atom.packages.activatePackage(
- 'package-with-activation-hooks'
- );
- expect(Package.prototype.requireMainModule.callCount).toBe(0);
- atom.packages.triggerActivationHook(
- 'language-fictitious:grammar-used'
- );
- atom.packages.triggerDeferredActivationHooks();
- await promise;
- expect(Package.prototype.requireMainModule.callCount).toBe(1);
- });
- it('does not double register activation hooks when deactivating and reactivating', async () => {
- promise = atom.packages.activatePackage(
- 'package-with-activation-hooks'
- );
- expect(mainModule.activate.callCount).toBe(0);
- atom.packages.triggerActivationHook(
- 'language-fictitious:grammar-used'
- );
- atom.packages.triggerDeferredActivationHooks();
- await promise;
- expect(mainModule.activate.callCount).toBe(1);
- await atom.packages.deactivatePackage(
- 'package-with-activation-hooks'
- );
- promise = atom.packages.activatePackage(
- 'package-with-activation-hooks'
- );
- atom.packages.triggerActivationHook(
- 'language-fictitious:grammar-used'
- );
- atom.packages.triggerDeferredActivationHooks();
- await promise;
- expect(mainModule.activate.callCount).toBe(2);
- });
- it('activates the package immediately when activationHooks is empty', async () => {
- mainModule = require('./fixtures/packages/package-with-empty-activation-hooks/index');
- spyOn(mainModule, 'activate').andCallThrough();
- expect(Package.prototype.requireMainModule.callCount).toBe(0);
- await atom.packages.activatePackage(
- 'package-with-empty-activation-hooks'
- );
- expect(mainModule.activate.callCount).toBe(1);
- expect(Package.prototype.requireMainModule.callCount).toBe(1);
- });
- it('activates the package immediately if the activation hook had already been triggered', async () => {
- atom.packages.triggerActivationHook(
- 'language-fictitious:grammar-used'
- );
- atom.packages.triggerDeferredActivationHooks();
- expect(Package.prototype.requireMainModule.callCount).toBe(0);
- await atom.packages.activatePackage('package-with-activation-hooks');
- expect(mainModule.activate.callCount).toBe(1);
- expect(Package.prototype.requireMainModule.callCount).toBe(1);
- });
- });
- describe('when the package metadata includes `workspaceOpeners`', () => {
- let mainModule, promise;
- beforeEach(() => {
- mainModule = require('./fixtures/packages/package-with-workspace-openers/index');
- spyOn(mainModule, 'activate').andCallThrough();
- });
- it('defers requiring/activating the main module until a registered opener is called', async () => {
- promise = atom.packages.activatePackage(
- 'package-with-workspace-openers'
- );
- expect(Package.prototype.requireMainModule.callCount).toBe(0);
- atom.workspace.open('atom://fictitious');
- await promise;
- expect(Package.prototype.requireMainModule.callCount).toBe(1);
- expect(mainModule.openerCount).toBe(1);
- });
- it('activates the package immediately when the events are empty', async () => {
- mainModule = require('./fixtures/packages/package-with-empty-workspace-openers/index');
- spyOn(mainModule, 'activate').andCallThrough();
- atom.packages.activatePackage('package-with-empty-workspace-openers');
- expect(mainModule.activate.callCount).toBe(1);
- });
- });
- });
- describe('when the package has no main module', () => {
- it('does not throw an exception', () => {
- spyOn(console, 'error');
- spyOn(console, 'warn').andCallThrough();
- expect(() =>
- atom.packages.activatePackage('package-without-module')
- ).not.toThrow();
- expect(console.error).not.toHaveBeenCalled();
- expect(console.warn).not.toHaveBeenCalled();
- });
- });
- describe('when the package does not export an activate function', () => {
- it('activates the package and does not throw an exception or log a warning', async () => {
- spyOn(console, 'warn');
- await atom.packages.activatePackage('package-with-no-activate');
- expect(console.warn).not.toHaveBeenCalled();
- });
- });
- it("passes the activate method the package's previously serialized state if it exists", async () => {
- const pack = await atom.packages.activatePackage(
- 'package-with-serialization'
- );
- expect(pack.mainModule.someNumber).not.toBe(77);
- pack.mainModule.someNumber = 77;
- atom.packages.serializePackage('package-with-serialization');
- await atom.packages.deactivatePackage('package-with-serialization');
- spyOn(pack.mainModule, 'activate').andCallThrough();
- await atom.packages.activatePackage('package-with-serialization');
- expect(pack.mainModule.activate).toHaveBeenCalledWith({ someNumber: 77 });
- });
- it('invokes ::onDidActivatePackage listeners with the activated package', async () => {
- let activatedPackage;
- atom.packages.onDidActivatePackage(pack => {
- activatedPackage = pack;
- });
- await atom.packages.activatePackage('package-with-main');
- expect(activatedPackage.name).toBe('package-with-main');
- });
- describe("when the package's main module throws an error on load", () => {
- it('adds a notification instead of throwing an exception', () => {
- spyOn(atom, 'inSpecMode').andReturn(false);
- atom.config.set('core.disabledPackages', []);
- const addErrorHandler = jasmine.createSpy();
- atom.notifications.onDidAddNotification(addErrorHandler);
- expect(() =>
- atom.packages.activatePackage('package-that-throws-an-exception')
- ).not.toThrow();
- expect(addErrorHandler.callCount).toBe(1);
- expect(addErrorHandler.argsForCall[0][0].message).toContain(
- 'Failed to load the package-that-throws-an-exception package'
- );
- expect(addErrorHandler.argsForCall[0][0].options.packageName).toEqual(
- 'package-that-throws-an-exception'
- );
- });
- it('re-throws the exception in test mode', () => {
- atom.config.set('core.disabledPackages', []);
- expect(() =>
- atom.packages.activatePackage('package-that-throws-an-exception')
- ).toThrow('This package throws an exception');
- });
- });
- describe('when the package is not found', () => {
- it('rejects the promise', async () => {
- spyOn(console, 'warn');
- atom.config.set('core.disabledPackages', []);
- try {
- await atom.packages.activatePackage('this-doesnt-exist');
- expect('Error to be thrown').toBe('');
- } catch (error) {
- expect(console.warn.callCount).toBe(1);
- expect(error.message).toContain(
- "Failed to load package 'this-doesnt-exist'"
- );
- }
- });
- });
- describe('keymap loading', () => {
- describe("when the metadata does not contain a 'keymaps' manifest", () => {
- it('loads all the .cson/.json files in the keymaps directory', async () => {
- const element1 = createTestElement('test-1');
- const element2 = createTestElement('test-2');
- const element3 = createTestElement('test-3');
- expect(
- atom.keymaps.findKeyBindings({
- keystrokes: 'ctrl-z',
- target: element1
- })
- ).toHaveLength(0);
- expect(
- atom.keymaps.findKeyBindings({
- keystrokes: 'ctrl-z',
- target: element2
- })
- ).toHaveLength(0);
- expect(
- atom.keymaps.findKeyBindings({
- keystrokes: 'ctrl-z',
- target: element3
- })
- ).toHaveLength(0);
- await atom.packages.activatePackage('package-with-keymaps');
- expect(
- atom.keymaps.findKeyBindings({
- keystrokes: 'ctrl-z',
- target: element1
- })[0].command
- ).toBe('test-1');
- expect(
- atom.keymaps.findKeyBindings({
- keystrokes: 'ctrl-z',
- target: element2
- })[0].command
- ).toBe('test-2');
- expect(
- atom.keymaps.findKeyBindings({
- keystrokes: 'ctrl-z',
- target: element3
- })
- ).toHaveLength(0);
- });
- });
- describe("when the metadata contains a 'keymaps' manifest", () => {
- it('loads only the keymaps specified by the manifest, in the specified order', async () => {
- const element1 = createTestElement('test-1');
- const element3 = createTestElement('test-3');
- expect(
- atom.keymaps.findKeyBindings({
- keystrokes: 'ctrl-z',
- target: element1
- })
- ).toHaveLength(0);
- await atom.packages.activatePackage('package-with-keymaps-manifest');
- expect(
- atom.keymaps.findKeyBindings({
- keystrokes: 'ctrl-z',
- target: element1
- })[0].command
- ).toBe('keymap-1');
- expect(
- atom.keymaps.findKeyBindings({
- keystrokes: 'ctrl-n',
- target: element1
- })[0].command
- ).toBe('keymap-2');
- expect(
- atom.keymaps.findKeyBindings({
- keystrokes: 'ctrl-y',
- target: element3
- })
- ).toHaveLength(0);
- });
- });
- describe('when the keymap file is empty', () => {
- it('does not throw an error on activation', async () => {
- await atom.packages.activatePackage('package-with-empty-keymap');
- expect(
- atom.packages.isPackageActive('package-with-empty-keymap')
- ).toBe(true);
- });
- });
- describe("when the package's keymaps have been disabled", () => {
- it('does not add the keymaps', async () => {
- const element1 = createTestElement('test-1');
- expect(
- atom.keymaps.findKeyBindings({
- keystrokes: 'ctrl-z',
- target: element1
- })
- ).toHaveLength(0);
- atom.config.set('core.packagesWithKeymapsDisabled', [
- 'package-with-keymaps-manifest'
- ]);
- await atom.packages.activatePackage('package-with-keymaps-manifest');
- expect(
- atom.keymaps.findKeyBindings({
- keystrokes: 'ctrl-z',
- target: element1
- })
- ).toHaveLength(0);
- });
- });
- describe('when setting core.packagesWithKeymapsDisabled', () => {
- it("ignores package names in the array that aren't loaded", () => {
- atom.packages.observePackagesWithKeymapsDisabled();
- expect(() =>
- atom.config.set('core.packagesWithKeymapsDisabled', [
- 'package-does-not-exist'
- ])
- ).not.toThrow();
- expect(() =>
- atom.config.set('core.packagesWithKeymapsDisabled', [])
- ).not.toThrow();
- });
- });
- describe("when the package's keymaps are disabled and re-enabled after it is activated", () => {
- it('removes and re-adds the keymaps', async () => {
- const element1 = createTestElement('test-1');
- atom.packages.observePackagesWithKeymapsDisabled();
- await atom.packages.activatePackage('package-with-keymaps-manifest');
- atom.config.set('core.packagesWithKeymapsDisabled', [
- 'package-with-keymaps-manifest'
- ]);
- expect(
- atom.keymaps.findKeyBindings({
- keystrokes: 'ctrl-z',
- target: element1
- })
- ).toHaveLength(0);
- atom.config.set('core.packagesWithKeymapsDisabled', []);
- expect(
- atom.keymaps.findKeyBindings({
- keystrokes: 'ctrl-z',
- target: element1
- })[0].command
- ).toBe('keymap-1');
- });
- });
- describe('when the package is de-activated and re-activated', () => {
- let element, events, userKeymapPath;
- beforeEach(() => {
- userKeymapPath = path.join(temp.mkdirSync(), 'user-keymaps.cson');
- spyOn(atom.keymaps, 'getUserKeymapPath').andReturn(userKeymapPath);
- element = createTestElement('test-1');
- jasmine.attachToDOM(element);
- events = [];
- element.addEventListener('user-command', e => events.push(e));
- element.addEventListener('test-1', e => events.push(e));
- });
- afterEach(() => {
- element.remove();
- // Avoid leaking user keymap subscription
- atom.keymaps.watchSubscriptions[userKeymapPath].dispose();
- delete atom.keymaps.watchSubscriptions[userKeymapPath];
- temp.cleanupSync();
- });
- it("doesn't override user-defined keymaps", async () => {
- fs.writeFileSync(
- userKeymapPath,
- `".test-1": {"ctrl-z": "user-command"}`
- );
- atom.keymaps.loadUserKeymap();
- await atom.packages.activatePackage('package-with-keymaps');
- atom.keymaps.handleKeyboardEvent(
- buildKeydownEvent('z', { ctrl: true, target: element })
- );
- expect(events.length).toBe(1);
- expect(events[0].type).toBe('user-command');
- await atom.packages.deactivatePackage('package-with-keymaps');
- await atom.packages.activatePackage('package-with-keymaps');
- atom.keymaps.handleKeyboardEvent(
- buildKeydownEvent('z', { ctrl: true, target: element })
- );
- expect(events.length).toBe(2);
- expect(events[1].type).toBe('user-command');
- });
- });
- });
- describe('menu loading', () => {
- beforeEach(() => {
- atom.contextMenu.definitions = [];
- atom.menu.template = [];
- });
- describe("when the metadata does not contain a 'menus' manifest", () => {
- it('loads all the .cson/.json files in the menus directory', async () => {
- const element = createTestElement('test-1');
- expect(atom.contextMenu.templateForElement(element)).toEqual([]);
- await atom.packages.activatePackage('package-with-menus');
- expect(atom.menu.template.length).toBe(2);
- expect(atom.menu.template[0].label).toBe('Second to Last');
- expect(atom.menu.template[1].label).toBe('Last');
- expect(atom.contextMenu.templateForElement(element)[0].label).toBe(
- 'Menu item 1'
- );
- expect(atom.contextMenu.templateForElement(element)[1].label).toBe(
- 'Menu item 2'
- );
- expect(atom.contextMenu.templateForElement(element)[2].label).toBe(
- 'Menu item 3'
- );
- });
- });
- describe("when the metadata contains a 'menus' manifest", () => {
- it('loads only the menus specified by the manifest, in the specified order', async () => {
- const element = createTestElement('test-1');
- expect(atom.contextMenu.templateForElement(element)).toEqual([]);
- await atom.packages.activatePackage('package-with-menus-manifest');
- expect(atom.menu.template[0].label).toBe('Second to Last');
- expect(atom.menu.template[1].label).toBe('Last');
- expect(atom.contextMenu.templateForElement(element)[0].label).toBe(
- 'Menu item 2'
- );
- expect(atom.contextMenu.templateForElement(element)[1].label).toBe(
- 'Menu item 1'
- );
- expect(
- atom.contextMenu.templateForElement(element)[2]
- ).toBeUndefined();
- });
- });
- describe('when the menu file is empty', () => {
- it('does not throw an error on activation', async () => {
- await atom.packages.activatePackage('package-with-empty-menu');
- expect(atom.packages.isPackageActive('package-with-empty-menu')).toBe(
- true
- );
- });
- });
- });
- describe('stylesheet loading', () => {
- describe("when the metadata contains a 'styleSheets' manifest", () => {
- it('loads style sheets from the styles directory as specified by the manifest', async () => {
- const one = require.resolve(
- './fixtures/packages/package-with-style-sheets-manifest/styles/1.css'
- );
- const two = require.resolve(
- './fixtures/packages/package-with-style-sheets-manifest/styles/2.less'
- );
- const three = require.resolve(
- './fixtures/packages/package-with-style-sheets-manifest/styles/3.css'
- );
- expect(atom.themes.stylesheetElementForId(one)).toBeNull();
- expect(atom.themes.stylesheetElementForId(two)).toBeNull();
- expect(atom.themes.stylesheetElementForId(three)).toBeNull();
- await atom.packages.activatePackage(
- 'package-with-style-sheets-manifest'
- );
- expect(atom.themes.stylesheetElementForId(one)).not.toBeNull();
- expect(atom.themes.stylesheetElementForId(two)).not.toBeNull();
- expect(atom.themes.stylesheetElementForId(three)).toBeNull();
- expect(
- getComputedStyle(document.querySelector('#jasmine-content'))
- .fontSize
- ).toBe('1px');
- });
- });
- describe("when the metadata does not contain a 'styleSheets' manifest", () => {
- it('loads all style sheets from the styles directory', async () => {
- const one = require.resolve(
- './fixtures/packages/package-with-styles/styles/1.css'
- );
- const two = require.resolve(
- './fixtures/packages/package-with-styles/styles/2.less'
- );
- const three = require.resolve(
- './fixtures/packages/package-with-styles/styles/3.test-context.css'
- );
- const four = require.resolve(
- './fixtures/packages/package-with-styles/styles/4.css'
- );
- expect(atom.themes.stylesheetElementForId(one)).toBeNull();
- expect(atom.themes.stylesheetElementForId(two)).toBeNull();
- expect(atom.themes.stylesheetElementForId(three)).toBeNull();
- expect(atom.themes.stylesheetElementForId(four)).toBeNull();
- await atom.packages.activatePackage('package-with-styles');
- expect(atom.themes.stylesheetElementForId(one)).not.toBeNull();
- expect(atom.themes.stylesheetElementForId(two)).not.toBeNull();
- expect(atom.themes.stylesheetElementForId(three)).not.toBeNull();
- expect(atom.themes.stylesheetElementForId(four)).not.toBeNull();
- expect(
- getComputedStyle(document.querySelector('#jasmine-content'))
- .fontSize
- ).toBe('3px');
- });
- });
- it("assigns the stylesheet's context based on the filename", async () => {
- await atom.packages.activatePackage('package-with-styles');
- let count = 0;
- for (let styleElement of atom.styles.getStyleElements()) {
- if (styleElement.sourcePath.match(/1.css/)) {
- expect(styleElement.context).toBe(undefined);
- count++;
- }
- if (styleElement.sourcePath.match(/2.less/)) {
- expect(styleElement.context).toBe(undefined);
- count++;
- }
- if (styleElement.sourcePath.match(/3.test-context.css/)) {
- expect(styleElement.context).toBe('test-context');
- count++;
- }
- if (styleElement.sourcePath.match(/4.css/)) {
- expect(styleElement.context).toBe(undefined);
- count++;
- }
- }
- expect(count).toBe(4);
- });
- });
- describe('grammar loading', () => {
- it("loads the package's grammars", async () => {
- await atom.packages.activatePackage('package-with-grammars');
- expect(atom.grammars.selectGrammar('a.alot').name).toBe('Alot');
- expect(atom.grammars.selectGrammar('a.alittle').name).toBe('Alittle');
- });
- it('loads any tree-sitter grammars defined in the package', async () => {
- atom.config.set('core.useTreeSitterParsers', true);
- await atom.packages.activatePackage('package-with-tree-sitter-grammar');
- const grammar = atom.grammars.selectGrammar('test.somelang');
- expect(grammar.name).toBe('Some Language');
- await grammar.getQuery('highlightsQuery');
- expect(grammar.highlightsQuery.includes('(empty)')).toBe(true);
- });
- });
- describe('scoped-property loading', () => {
- it('loads the scoped properties', async () => {
- await atom.packages.activatePackage('package-with-settings');
- expect(
- atom.config.get('editor.increaseIndentPattern', {
- scope: ['.source.omg']
- })
- ).toBe('^a');
- });
- });
- describe('URI handler registration', () => {
- it("registers the package's specified URI handler", async () => {
- const uri = 'atom://package-with-uri-handler/some/url?with=args';
- const mod = require('./fixtures/packages/package-with-uri-handler');
- spyOn(mod, 'handleURI');
- spyOn(atom.packages, 'hasLoadedInitialPackages').andReturn(true);
- const activationPromise = atom.packages.activatePackage(
- 'package-with-uri-handler'
- );
- atom.dispatchURIMessage(uri);
- await activationPromise;
- expect(mod.handleURI).toHaveBeenCalledWith(url.parse(uri, true), uri);
- });
- });
- describe('service registration', () => {
- it("registers the package's provided and consumed services", async () => {
- const consumerModule = require('./fixtures/packages/package-with-consumed-services');
- let firstServiceV3Disposed = false;
- let firstServiceV4Disposed = false;
- let secondServiceDisposed = false;
- spyOn(consumerModule, 'consumeFirstServiceV3').andReturn(
- new Disposable(() => {
- firstServiceV3Disposed = true;
- })
- );
- spyOn(consumerModule, 'consumeFirstServiceV4').andReturn(
- new Disposable(() => {
- firstServiceV4Disposed = true;
- })
- );
- spyOn(consumerModule, 'consumeSecondService').andReturn(
- new Disposable(() => {
- secondServiceDisposed = true;
- })
- );
- await atom.packages.activatePackage('package-with-consumed-services');
- await atom.packages.activatePackage('package-with-provided-services');
- expect(consumerModule.consumeFirstServiceV3.callCount).toBe(1);
- expect(consumerModule.consumeFirstServiceV3).toHaveBeenCalledWith(
- 'first-service-v3'
- );
- expect(consumerModule.consumeFirstServiceV4).toHaveBeenCalledWith(
- 'first-service-v4'
- );
- expect(consumerModule.consumeSecondService).toHaveBeenCalledWith(
- 'second-service'
- );
- consumerModule.consumeFirstServiceV3.reset();
- consumerModule.consumeFirstServiceV4.reset();
- consumerModule.consumeSecondService.reset();
- await atom.packages.deactivatePackage('package-with-provided-services');
- expect(firstServiceV3Disposed).toBe(true);
- expect(firstServiceV4Disposed).toBe(true);
- expect(secondServiceDisposed).toBe(true);
- await atom.packages.deactivatePackage('package-with-consumed-services');
- await atom.packages.activatePackage('package-with-provided-services');
- expect(consumerModule.consumeFirstServiceV3).not.toHaveBeenCalled();
- expect(consumerModule.consumeFirstServiceV4).not.toHaveBeenCalled();
- expect(consumerModule.consumeSecondService).not.toHaveBeenCalled();
- });
- it('ignores provided and consumed services that do not exist', async () => {
- const addErrorHandler = jasmine.createSpy();
- atom.notifications.onDidAddNotification(addErrorHandler);
- await atom.packages.activatePackage(
- 'package-with-missing-consumed-services'
- );
- await atom.packages.activatePackage(
- 'package-with-missing-provided-services'
- );
- expect(
- atom.packages.isPackageActive(
- 'package-with-missing-consumed-services'
- )
- ).toBe(true);
- expect(
- atom.packages.isPackageActive(
- 'package-with-missing-provided-services'
- )
- ).toBe(true);
- expect(addErrorHandler.callCount).toBe(0);
- });
- });
- });
- describe('::serialize', () => {
- it('does not serialize packages that threw an error during activation', async () => {
- spyOn(atom, 'inSpecMode').andReturn(false);
- spyOn(console, 'warn');
- const badPack = await atom.packages.activatePackage(
- 'package-that-throws-on-activate'
- );
- spyOn(badPack.mainModule, 'serialize').andCallThrough();
- atom.packages.serialize();
- expect(badPack.mainModule.serialize).not.toHaveBeenCalled();
- });
- it("absorbs exceptions that are thrown by the package module's serialize method", async () => {
- spyOn(console, 'error');
- await atom.packages.activatePackage('package-with-serialize-error');
- await atom.packages.activatePackage('package-with-serialization');
- atom.packages.serialize();
- expect(
- atom.packages.packageStates['package-with-serialize-error']
- ).toBeUndefined();
- expect(atom.packages.packageStates['package-with-serialization']).toEqual(
- { someNumber: 1 }
- );
- expect(console.error).toHaveBeenCalled();
- });
- });
- describe('::deactivatePackages()', () => {
- it('deactivates all packages but does not serialize them', async () => {
- const pack1 = await atom.packages.activatePackage(
- 'package-with-deactivate'
- );
- const pack2 = await atom.packages.activatePackage(
- 'package-with-serialization'
- );
- spyOn(pack1.mainModule, 'deactivate');
- spyOn(pack2.mainModule, 'serialize');
- await atom.packages.deactivatePackages();
- expect(pack1.mainModule.deactivate).toHaveBeenCalled();
- expect(pack2.mainModule.serialize).not.toHaveBeenCalled();
- });
- });
- describe('::deactivatePackage(id)', () => {
- afterEach(() => atom.packages.unloadPackages());
- it("calls `deactivate` on the package's main module if activate was successful", async () => {
- spyOn(atom, 'inSpecMode').andReturn(false);
- const pack = await atom.packages.activatePackage(
- 'package-with-deactivate'
- );
- expect(
- atom.packages.isPackageActive('package-with-deactivate')
- ).toBeTruthy();
- spyOn(pack.mainModule, 'deactivate').andCallThrough();
- await atom.packages.deactivatePackage('package-with-deactivate');
- expect(pack.mainModule.deactivate).toHaveBeenCalled();
- expect(atom.packages.isPackageActive('package-with-module')).toBeFalsy();
- spyOn(console, 'warn');
- const badPack = await atom.packages.activatePackage(
- 'package-that-throws-on-activate'
- );
- expect(
- atom.packages.isPackageActive('package-that-throws-on-activate')
- ).toBeTruthy();
- spyOn(badPack.mainModule, 'deactivate').andCallThrough();
- await atom.packages.deactivatePackage('package-that-throws-on-activate');
- expect(badPack.mainModule.deactivate).not.toHaveBeenCalled();
- expect(
- atom.packages.isPackageActive('package-that-throws-on-activate')
- ).toBeFalsy();
- });
- it("absorbs exceptions that are thrown by the package module's deactivate method", async () => {
- spyOn(console, 'error');
- await atom.packages.activatePackage('package-that-throws-on-deactivate');
- await atom.packages.deactivatePackage(
- 'package-that-throws-on-deactivate'
- );
- expect(console.error).toHaveBeenCalled();
- });
- it("removes the package's grammars", async () => {
- await atom.packages.activatePackage('package-with-grammars');
- await atom.packages.deactivatePackage('package-with-grammars');
- expect(atom.grammars.selectGrammar('a.alot').name).toBe('Null Grammar');
- expect(atom.grammars.selectGrammar('a.alittle').name).toBe(
- 'Null Grammar'
- );
- });
- it("removes the package's keymaps", async () => {
- await atom.packages.activatePackage('package-with-keymaps');
- await atom.packages.deactivatePackage('package-with-keymaps');
- expect(
- atom.keymaps.findKeyBindings({
- keystrokes: 'ctrl-z',
- target: createTestElement('test-1')
- })
- ).toHaveLength(0);
- expect(
- atom.keymaps.findKeyBindings({
- keystrokes: 'ctrl-z',
- target: createTestElement('test-2')
- })
- ).toHaveLength(0);
- });
- it("removes the package's stylesheets", async () => {
- await atom.packages.activatePackage('package-with-styles');
- await atom.packages.deactivatePackage('package-with-styles');
- const one = require.resolve(
- './fixtures/packages/package-with-style-sheets-manifest/styles/1.css'
- );
- const two = require.resolve(
- './fixtures/packages/package-with-style-sheets-manifest/styles/2.less'
- );
- const three = require.resolve(
- './fixtures/packages/package-with-style-sheets-manifest/styles/3.css'
- );
- expect(atom.themes.stylesheetElementForId(one)).not.toExist();
- expect(atom.themes.stylesheetElementForId(two)).not.toExist();
- expect(atom.themes.stylesheetElementForId(three)).not.toExist();
- });
- it("removes the package's scoped-properties", async () => {
- await atom.packages.activatePackage('package-with-settings');
- expect(
- atom.config.get('editor.increaseIndentPattern', {
- scope: ['.source.omg']
- })
- ).toBe('^a');
- await atom.packages.deactivatePackage('package-with-settings');
- expect(
- atom.config.get('editor.increaseIndentPattern', {
- scope: ['.source.omg']
- })
- ).toBeUndefined();
- });
- it('invokes ::onDidDeactivatePackage listeners with the deactivated package', async () => {
- await atom.packages.activatePackage('package-with-main');
- let deactivatedPackage;
- atom.packages.onDidDeactivatePackage(pack => {
- deactivatedPackage = pack;
- });
- await atom.packages.deactivatePackage('package-with-main');
- expect(deactivatedPackage.name).toBe('package-with-main');
- });
- });
- describe('::activate()', () => {
- beforeEach(() => {
- spyOn(atom, 'inSpecMode').andReturn(false);
- jasmine.snapshotDeprecations();
- spyOn(console, 'warn');
- atom.packages.loadPackages();
- const loadedPackages = atom.packages.getLoadedPackages();
- expect(loadedPackages.length).toBeGreaterThan(0);
- });
- afterEach(async () => {
- await atom.packages.deactivatePackages();
- atom.packages.unloadPackages();
- jasmine.restoreDeprecationsSnapshot();
- });
- it('sets hasActivatedInitialPackages', async () => {
- spyOn(atom.styles, 'getUserStyleSheetPath').andReturn(null);
- spyOn(atom.packages, 'activatePackages');
- expect(atom.packages.hasActivatedInitialPackages()).toBe(false);
- await atom.packages.activate();
- expect(atom.packages.hasActivatedInitialPackages()).toBe(true);
- });
- it('activates all the packages, and none of the themes', () => {
- const packageActivator = spyOn(atom.packages, 'activatePackages');
- const themeActivator = spyOn(atom.themes, 'activatePackages');
- atom.packages.activate();
- expect(packageActivator).toHaveBeenCalled();
- expect(themeActivator).toHaveBeenCalled();
- const packages = packageActivator.mostRecentCall.args[0];
- for (let pack of packages) {
- expect(['atom', 'textmate']).toContain(pack.getType());
- }
- const themes = themeActivator.mostRecentCall.args[0];
- themes.map(theme => expect(['theme']).toContain(theme.getType()));
- });
- it('calls callbacks registered with ::onDidActivateInitialPackages', async () => {
- const package1 = atom.packages.loadPackage('package-with-main');
- const package2 = atom.packages.loadPackage('package-with-index');
- const package3 = atom.packages.loadPackage(
- 'package-with-activation-commands'
- );
- spyOn(atom.packages, 'getLoadedPackages').andReturn([
- package1,
- package2,
- package3
- ]);
- spyOn(atom.themes, 'activatePackages');
- atom.packages.activate();
- await new Promise(resolve =>
- atom.packages.onDidActivateInitialPackages(resolve)
- );
- jasmine.unspy(atom.packages, 'getLoadedPackages');
- expect(atom.packages.getActivePackages().includes(package1)).toBe(true);
- expect(atom.packages.getActivePackages().includes(package2)).toBe(true);
- expect(atom.packages.getActivePackages().includes(package3)).toBe(false);
- });
- });
- describe('::enablePackage(id) and ::disablePackage(id)', () => {
- describe('with packages', () => {
- it('enables a disabled package', async () => {
- const packageName = 'package-with-main';
- atom.config.pushAtKeyPath('core.disabledPackages', packageName);
- atom.packages.observeDisabledPackages();
- expect(atom.config.get('core.disabledPackages')).toContain(packageName);
- const pack = atom.packages.enablePackage(packageName);
- await new Promise(resolve =>
- atom.packages.onDidActivatePackage(resolve)
- );
- expect(atom.packages.getLoadedPackages()).toContain(pack);
- expect(atom.packages.getActivePackages()).toContain(pack);
- expect(atom.config.get('core.disabledPackages')).not.toContain(
- packageName
- );
- });
- it('disables an enabled package', async () => {
- const packageName = 'package-with-main';
- const pack = await atom.packages.activatePackage(packageName);
- atom.packages.observeDisabledPackages();
- expect(atom.config.get('core.disabledPackages')).not.toContain(
- packageName
- );
- await new Promise(resolve => {
- atom.packages.onDidDeactivatePackage(resolve);
- atom.packages.disablePackage(packageName);
- });
- expect(atom.packages.getActivePackages()).not.toContain(pack);
- expect(atom.config.get('core.disabledPackages')).toContain(packageName);
- });
- it('returns null if the package cannot be loaded', () => {
- spyOn(console, 'warn');
- expect(atom.packages.enablePackage('this-doesnt-exist')).toBeNull();
- expect(console.warn.callCount).toBe(1);
- });
- it('does not disable an already disabled package', () => {
- const packageName = 'package-with-main';
- atom.config.pushAtKeyPath('core.disabledPackages', packageName);
- atom.packages.observeDisabledPackages();
- expect(atom.config.get('core.disabledPackages')).toContain(packageName);
- atom.packages.disablePackage(packageName);
- const packagesDisabled = atom.config
- .get('core.disabledPackages')
- .filter(pack => pack === packageName);
- expect(packagesDisabled.length).toEqual(1);
- });
- });
- describe('with themes', () => {
- beforeEach(() => atom.themes.activateThemes());
- afterEach(() => atom.themes.deactivateThemes());
- it('enables and disables a theme', async () => {
- const packageName = 'theme-with-package-file';
- expect(atom.config.get('core.themes')).not.toContain(packageName);
- expect(atom.config.get('core.disabledPackages')).not.toContain(
- packageName
- );
- // enabling of theme
- const pack = atom.packages.enablePackage(packageName);
- await new Promise(resolve =>
- atom.packages.onDidActivatePackage(resolve)
- );
- expect(atom.packages.isPackageActive(packageName)).toBe(true);
- expect(atom.config.get('core.themes')).toContain(packageName);
- expect(atom.config.get('core.disabledPackages')).not.toContain(
- packageName
- );
- await new Promise(resolve => {
- atom.themes.onDidChangeActiveThemes(resolve);
- atom.packages.disablePackage(packageName);
- });
- expect(atom.packages.getActivePackages()).not.toContain(pack);
- expect(atom.config.get('core.themes')).not.toContain(packageName);
- expect(atom.config.get('core.themes')).not.toContain(packageName);
- expect(atom.config.get('core.disabledPackages')).not.toContain(
- packageName
- );
- });
- });
- });
- describe('::getAvailablePackageNames', () => {
- it('detects a symlinked package', () => {
- const packageSymLinkedSource = path.join(
- __dirname,
- 'fixtures',
- 'packages',
- 'folder',
- 'package-symlinked'
- );
- const destination = path.join(
- atom.packages.getPackageDirPaths()[0],
- 'package-symlinked'
- );
- if (!fs.isDirectorySync(destination)) {
- fs.symlinkSync(packageSymLinkedSource, destination, 'junction');
- }
- const availablePackages = atom.packages.getAvailablePackageNames();
- expect(availablePackages.includes('package-symlinked')).toBe(true);
- fs.removeSync(destination);
- });
- });
- });
|