text: /* globals assert */
  
  const path = require('path');
  const fs = require('fs-plus');
  const url = require('url');
  const { EventEmitter } = require('events');
  const temp = require('temp').track();
  const { sandbox } = require('sinon');
  const dedent = require('dedent');
  
  const AtomWindow = require('../../src/main-process/atom-window');
  const { emitterEventPromise } = require('../async-spec-helpers');
  
  describe('AtomWindow', function() {
    let sinon, app, service;
  
    beforeEach(function() {
      sinon = sandbox.create();
      app = new StubApplication(sinon);
      service = new StubRecoveryService(sinon);
    });
  
    afterEach(function() {
      sinon.restore();
    });
  
    describe('creating a real window', function() {
      let resourcePath, windowInitializationScript, atomHome;
      let original;
  
      this.timeout(10 * 1000);
  
      beforeEach(async function() {
        original = {
          ATOM_HOME: process.env.ATOM_HOME,
          ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT:
            process.env.ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT
        };
  
        resourcePath = path.resolve(__dirname, '../..');
  
        windowInitializationScript = require.resolve(
          path.join(resourcePath, 'src/initialize-application-window')
        );
  
        atomHome = await new Promise((resolve, reject) => {
          temp.mkdir('launch-', (err, rootPath) => {
            if (err) {
              reject(err);
            } else {
              resolve(rootPath);
            }
          });
        });
  
        await new Promise((resolve, reject) => {
          const config = dedent`
            '*':
              core:
                automaticallyUpdate: false
                telemetryConsent: "no"
              welcome:
                showOnStartup: false
          `;
  
          fs.writeFile(
            path.join(atomHome, 'config.cson'),
            config,
            { encoding: 'utf8' },
            err => {
              if (err) {
                reject(err);
              } else {
                resolve();
              }
            }
          );
        });
  
        await new Promise((resolve, reject) => {
          fs.symlink(
            path.join(original.ATOM_HOME, 'compile-cache'),
            path.join(atomHome, 'compile-cache'),
            'junction',
            err => {
              if (err) {
                reject(err);
              } else {
                resolve();
              }
            }
          );
        });
  
        process.env.ATOM_HOME = atomHome;
        process.env.ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT = 'true';
      });
  
      afterEach(async function() {
        process.env.ATOM_HOME = original.ATOM_HOME;
        process.env.ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT =
          original.ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT;
      });
  
      it('creates a real, properly configured BrowserWindow', async function() {
        const w = new AtomWindow(app, service, {
          resourcePath,
          windowInitializationScript,
          headless: true,
          extra: 'extra-load-setting'
        });
        const { browserWindow } = w;
  
        assert.isFalse(browserWindow.isVisible());
        assert.strictEqual(browserWindow.getTitle(), 'Atom');
  
        const settings = JSON.parse(browserWindow.loadSettingsJSON);
        assert.strictEqual(settings.userSettings, 'stub-config');
        assert.strictEqual(settings.extra, 'extra-load-setting');
        assert.strictEqual(settings.resourcePath, resourcePath);
        assert.strictEqual(settings.atomHome, atomHome);
        assert.isFalse(settings.devMode);
        assert.isFalse(settings.safeMode);
        assert.isFalse(settings.clearWindowState);
  
        await emitterEventPromise(browserWindow, 'ready-to-show');
  
        assert.strictEqual(
          browserWindow.webContents.getURL(),
          url.format({
            protocol: 'file',
            pathname: `${resourcePath.replace(/\\/g, '/')}/static/index.html`,
            slashes: true
          })
        );
      });
    });
  
    describe('launch behavior', function() {
      if (process.platform === 'darwin') {
        it('sets titleBarStyle to "hidden" for a custom title bar on non-spec windows', function() {
          app.config['core.titleBar'] = 'custom';
  
          const { browserWindow: w0 } = new AtomWindow(app, service, {
            browserWindowConstructor: StubBrowserWindow
          });
          assert.strictEqual(w0.options.titleBarStyle, 'hidden');
  
          const { browserWindow: w1 } = new AtomWindow(app, service, {
            browserWindowConstructor: StubBrowserWindow,
            isSpec: true
          });
          assert.isUndefined(w1.options.titleBarStyle);
        });
  
        it('sets titleBarStyle to "hiddenInset" for a custom inset title bar on non-spec windows', function() {
          app.config['core.titleBar'] = 'custom-inset';
  
          const { browserWindow: w0 } = new AtomWindow(app, service, {
            browserWindowConstructor: StubBrowserWindow
          });
          assert.strictEqual(w0.options.titleBarStyle, 'hiddenInset');
  
          const { browserWindow: w1 } = new AtomWindow(app, service, {
            browserWindowConstructor: StubBrowserWindow,
            isSpec: true
          });
          assert.isUndefined(w1.options.titleBarStyle);
        });
  
        it('sets frame to "false" for a hidden title bar on non-spec windows', function() {
          app.config['core.titleBar'] = 'hidden';
  
          const { browserWindow: w0 } = new AtomWindow(app, service, {
            browserWindowConstructor: StubBrowserWindow
          });
          assert.isFalse(w0.options.frame);
  
          const { browserWindow: w1 } = new AtomWindow(app, service, {
            browserWindowConstructor: StubBrowserWindow,
            isSpec: true
          });
          assert.isUndefined(w1.options.frame);
        });
      } else {
        it('ignores title bar style settings', function() {
          for (const value of ['custom', 'custom-inset', 'hidden']) {
            app.config['core.titleBar'] = value;
            const { browserWindow } = new AtomWindow(app, service, {
              browserWindowConstructor: StubBrowserWindow
            });
            assert.isUndefined(browserWindow.options.titleBarStyle);
            assert.isUndefined(browserWindow.options.frame);
          }
        });
      }
  
      it('opens initial locations', async function() {
        const locationsToOpen = [
          {
            pathToOpen: 'file.txt',
            initialLine: 1,
            initialColumn: 2,
            isDirectory: false,
            hasWaitSession: false
          },
          {
            pathToOpen: '/directory',
            initialLine: null,
            initialColumn: null,
            isDirectory: true,
            hasWaitSession: false
          }
        ];
  
        const w = new AtomWindow(app, service, {
          browserWindowConstructor: StubBrowserWindow,
          locationsToOpen
        });
        assert.deepEqual(w.projectRoots, ['/directory']);
  
        const loadPromise = emitterEventPromise(w, 'window:loaded');
        w.browserWindow.emit('window:loaded');
        await loadPromise;
  
        assert.deepEqual(w.browserWindow.sent, [
          ['message', 'open-locations', locationsToOpen]
        ]);
      });
  
      it('does not open an initial null location', async function() {
        const w = new AtomWindow(app, service, {
          browserWindowConstructor: StubBrowserWindow,
          locationsToOpen: [{ pathToOpen: null }]
        });
  
        const loadPromise = emitterEventPromise(w, 'window:loaded');
        w.browserWindow.emit('window:loaded');
        await loadPromise;
  
        assert.lengthOf(w.browserWindow.sent, 0);
      });
  
      it('does not open initial locations in spec mode', async function() {
        const w = new AtomWindow(app, service, {
          browserWindowConstructor: StubBrowserWindow,
          locationsToOpen: [{ pathToOpen: 'file.txt' }],
          isSpec: true
        });
  
        const loadPromise = emitterEventPromise(w, 'window:loaded');
        w.browserWindow.emit('window:loaded');
        await loadPromise;
  
        assert.lengthOf(w.browserWindow.sent, 0);
      });
  
      it('focuses the webView for specs', function() {
        const w = new AtomWindow(app, service, {
          browserWindowConstructor: StubBrowserWindow,
          isSpec: true
        });
  
        assert.isTrue(w.browserWindow.behavior.focusOnWebView);
      });
    });
  
    describe('project root tracking', function() {
      it('knows when it has no roots', function() {
        const w = new AtomWindow(app, service, {
          browserWindowConstructor: StubBrowserWindow
        });
        assert.isFalse(w.hasProjectPaths());
      });
  
      it('is initialized from directories in the initial locationsToOpen', function() {
        const locationsToOpen = [
          { pathToOpen: 'file.txt', exists: true, isFile: true },
          { pathToOpen: 'directory0', exists: true, isDirectory: true },
          { pathToOpen: 'directory1', exists: true, isDirectory: true },
          { pathToOpen: 'new-file.txt' },
          { pathToOpen: null }
        ];
  
        const w = new AtomWindow(app, service, {
          browserWindowConstructor: StubBrowserWindow,
          locationsToOpen
        });
  
        assert.deepEqual(w.projectRoots, ['directory0', 'directory1']);
        assert.isTrue(w.loadSettings.hasOpenFiles);
        assert.deepEqual(w.loadSettings.initialProjectRoots, [
          'directory0',
          'directory1'
        ]);
        assert.isTrue(w.hasProjectPaths());
      });
  
      it('is updated synchronously by openLocations', async function() {
        const locationsToOpen = [
          { pathToOpen: 'file.txt', isFile: true },
          { pathToOpen: 'directory1', isDirectory: true },
          { pathToOpen: 'directory0', isDirectory: true },
          { pathToOpen: 'directory0', isDirectory: true },
          { pathToOpen: 'new-file.txt' }
        ];
  
        const w = new AtomWindow(app, service, {
          browserWindowConstructor: StubBrowserWindow
        });
        assert.deepEqual(w.projectRoots, []);
  
        const promise = w.openLocations(locationsToOpen);
        assert.deepEqual(w.projectRoots, ['directory0', 'directory1']);
        w.resolveLoadedPromise();
        await promise;
      });
  
      it('is updated by setProjectRoots', function() {
        const locationsToOpen = [
          { pathToOpen: 'directory0', exists: true, isDirectory: true }
        ];
  
        const w = new AtomWindow(app, service, {
          browserWindowConstructor: StubBrowserWindow,
          locationsToOpen
        });
        assert.deepEqual(w.projectRoots, ['directory0']);
        assert.deepEqual(w.loadSettings.initialProjectRoots, ['directory0']);
  
        w.setProjectRoots(['directory1', 'directory0', 'directory2']);
        assert.deepEqual(w.projectRoots, [
          'directory0',
          'directory1',
          'directory2'
        ]);
        assert.deepEqual(w.loadSettings.initialProjectRoots, [
          'directory0',
          'directory1',
          'directory2'
        ]);
      });
  
      it('never reports that it owns the empty path', function() {
        const locationsToOpen = [
          { pathToOpen: 'directory0', exists: true, isDirectory: true },
          { pathToOpen: 'directory1', exists: true, isDirectory: true },
          { pathToOpen: null }
        ];
  
        const w = new AtomWindow(app, service, {
          browserWindowConstructor: StubBrowserWindow,
          locationsToOpen
        });
        assert.isFalse(w.containsLocation({ pathToOpen: null }));
      });
  
      it('discovers an exact path match', function() {
        const locationsToOpen = [
          { pathToOpen: 'directory0', exists: true, isDirectory: true },
          { pathToOpen: 'directory1', exists: true, isDirectory: true }
        ];
        const w = new AtomWindow(app, service, {
          browserWindowConstructor: StubBrowserWindow,
          locationsToOpen
        });
  
        assert.isTrue(w.containsLocation({ pathToOpen: 'directory0' }));
        assert.isFalse(w.containsLocation({ pathToOpen: 'directory2' }));
      });
  
      it('discovers the path of a file within any project root', function() {
        const locationsToOpen = [
          { pathToOpen: 'directory0', exists: true, isDirectory: true },
          { pathToOpen: 'directory1', exists: true, isDirectory: true }
        ];
        const w = new AtomWindow(app, service, {
          browserWindowConstructor: StubBrowserWindow,
          locationsToOpen
        });
  
        assert.isTrue(
          w.containsLocation({
            pathToOpen: path.join('directory0/file-0.txt'),
            exists: true,
            isFile: true
          })
        );
        assert.isTrue(
          w.containsLocation({
            pathToOpen: path.join('directory0/deep/file-0.txt'),
            exists: true,
            isFile: true
          })
        );
        assert.isFalse(
          w.containsLocation({
            pathToOpen: path.join('directory2/file-9.txt'),
            exists: true,
            isFile: true
          })
        );
        assert.isFalse(
          w.containsLocation({
            pathToOpen: path.join('directory2/deep/file-9.txt'),
            exists: true,
            isFile: true
          })
        );
      });
  
      it('reports that it owns nonexistent paths within a project root', function() {
        const locationsToOpen = [
          { pathToOpen: 'directory0', exists: true, isDirectory: true },
          { pathToOpen: 'directory1', exists: true, isDirectory: true }
        ];
        const w = new AtomWindow(app, service, {
          browserWindowConstructor: StubBrowserWindow,
          locationsToOpen
        });
  
        assert.isTrue(
          w.containsLocation({
            pathToOpen: path.join('directory0/file-1.txt'),
            exists: false
          })
        );
        assert.isTrue(
          w.containsLocation({
            pathToOpen: path.join('directory1/subdir/file-0.txt'),
            exists: false
          })
        );
      });
  
      it('never reports that it owns directories within a project root', function() {
        const locationsToOpen = [
          { pathToOpen: 'directory0', exists: true, isDirectory: true },
          { pathToOpen: 'directory1', exists: true, isDirectory: true }
        ];
        const w = new AtomWindow(app, service, {
          browserWindowConstructor: StubBrowserWindow,
          locationsToOpen
        });
  
        assert.isFalse(
          w.containsLocation({
            pathToOpen: path.join('directory0/subdir-0'),
            exists: true,
            isDirectory: true
          })
        );
      });
  
      it('checks a full list of paths and reports if it owns all of them', function() {
        const locationsToOpen = [
          { pathToOpen: 'directory0', exists: true, isDirectory: true },
          { pathToOpen: 'directory1', exists: true, isDirectory: true }
        ];
        const w = new AtomWindow(app, service, {
          browserWindowConstructor: StubBrowserWindow,
          locationsToOpen
        });
  
        assert.isTrue(
          w.containsLocations([
            { pathToOpen: 'directory0' },
            {
              pathToOpen: path.join('directory1/file-0.txt'),
              exists: true,
              isFile: true
            }
          ])
        );
        assert.isFalse(
          w.containsLocations([
            { pathToOpen: 'directory2' },
            { pathToOpen: 'directory0' }
          ])
        );
        assert.isFalse(
          w.containsLocations([
            { pathToOpen: 'directory2' },
            { pathToOpen: 'directory1' }
          ])
        );
      });
    });
  });
  
  class StubApplication {
    constructor(sinon) {
      this.config = {
        'core.titleBar': 'native',
        get: key => this.config[key] || null
      };
      this.configFile = {
        get() {
          return 'stub-config';
        }
      };
  
      this.removeWindow = sinon.spy();
      this.saveCurrentWindowOptions = sinon.spy();
    }
  }
  
  class StubRecoveryService {
    constructor(sinon) {
      this.didCloseWindow = sinon.spy();
      this.didCrashWindow = sinon.spy();
    }
  }
  
  class StubBrowserWindow extends EventEmitter {
    constructor(options) {
      super();
      this.options = options;
      this.sent = [];
      this.behavior = {
        focusOnWebView: false
      };
  
      this.webContents = new EventEmitter();
      this.webContents.send = (...args) => {
        this.sent.push(args);
      };
      this.webContents.setVisualZoomLevelLimits = () => {};
    }
  
    loadURL() {}
  
    focusOnWebView() {
      this.behavior.focusOnWebView = true;
    }
  }
  