<commit_before>import { clickTrigger } from 'ember-power-select/test-support/helpers';
import { click, fillIn, findAll, currentURL, find, visit, settled, waitUntil } from '@ember/test-helpers';
import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import authPage from 'vault/tests/pages/auth';
import { pollCluster } from 'vault/tests/helpers/poll-cluster';
import { create } from 'ember-cli-page-object';
import flashMessage from 'vault/tests/pages/components/flash-message';
import ss from 'vault/tests/pages/components/search-select';

const searchSelect = create(ss);
const flash = create(flashMessage);

const disableReplication = async (type, assert) => {
  // disable performance replication
  await visit(`/vault/replication/${type}`);

  if (findAll('[data-test-replication-link="manage"]').length) {
    await click('[data-test-replication-link="manage"]');

    await click('[data-test-disable-replication] button');

    const typeDisplay = type === 'dr' ? 'Disaster Recovery' : 'Performance';
    await fillIn('[data-test-confirmation-modal-input="disable"]', typeDisplay);
    await click('[data-test-confirm-button]');
    await settled(); // eslint-disable-line

    if (assert) {
      // bypassing for now -- remove if tests pass reliably
      // assert.equal(
      //   flash.latestMessage,
      //   'This cluster is having replication disabled. Vault will be unavailable for a brief period and will resume service shortly.',
      //   'renders info flash when disabled'
      // );
      assert.ok(
        await waitUntil(() => currentURL() === '/vault/replication'),
        'redirects to the replication page'
      );
    }
    await settled();
  }
};

module('Acceptance | Enterprise | replication', function (hooks) {
  setupApplicationTest(hooks);

  hooks.beforeEach(async function () {
    await authPage.login();
    await settled();
    await disableReplication('dr');
    await settled();
    await disableReplication('performance');
    await settled();
  });

  hooks.afterEach(async function () {
    await disableReplication('dr');
    await settled();
    await disableReplication('performance');
    await settled();
  });

  test('replication', async function (assert) {
    assert.expect(17);
    const secondaryName = 'firstSecondary';
    const mode = 'deny';
    let mountPath;

    // confirm unable to visit dr secondary details page when both replications are disabled
    await visit('/vault/replication-dr-promote/details');

    assert.dom('[data-test-component="empty-state"]').exists();
    assert
      .dom('[data-test-empty-state-title]')
      .includesText('Disaster Recovery secondary not set up', 'shows the correct title of the empty state');

    assert
      .dom('[data-test-empty-state-message]')
      .hasText(
        'This cluster has not been enabled as a Disaster Recovery Secondary. You can do so by enabling replication and adding a secondary from the Disaster Recovery Primary.',
        'renders default message specific to when no replication is enabled'
      );

    await visit('/vault/replication');

    assert.equal(currentURL(), '/vault/replication');

    // enable perf replication
    await click('[data-test-replication-type-select="performance"]');

    await fillIn('[data-test-replication-cluster-mode-select]', 'primary');

    await click('[data-test-replication-enable]');

    await pollCluster(this.owner);

    // confirm that the details dashboard shows
    assert.ok(await waitUntil(() => find('[data-test-replication-dashboard]')), 'details dashboard is shown');

    // add a secondary with a mount filter config
    await click('[data-test-replication-link="secondaries"]');

    await click('[data-test-secondary-add]');

    await fillIn('[data-test-replication-secondary-id]', secondaryName);

    await click('#deny');
    await clickTrigger();
    mountPath = searchSelect.options.objectAt(0).text;
    await searchSelect.options.objectAt(0).click();
    await click('[data-test-secondary-add]');

    await pollCluster(this.owner);
    // click into the added secondary's mount filter config
    await click('[data-test-replication-link="secondaries"]');

    await click('[data-test-popup-menu-trigger]');

    await click('[data-test-replication-path-filter-link]');

    assert.equal(currentURL(), `/vault/replication/performance/secondaries/config/show/${secondaryName}`);
    assert.dom('[data-test-mount-config-mode]').includesText(mode, 'show page renders the correct mode');
    assert
      .dom('[data-test-mount-config-paths]')
      .includesText(mountPath, 'show page renders the correct mount path');

    // delete config by choosing "no filter" in the edit screen
    await click('[data-test-replication-link="edit-mount-config"]');

    await click('#no-filtering');

    await click('[data-test-config-save]');
    await settled(); // eslint-disable-line

    assert.equal(
      flash.latestMessage,
      `The performance mount filter config for the secondary ${secondaryName} was successfully deleted.`,
      'renders success flash upon deletion'
    );
    assert.equal(
      currentURL(),
      `/vault/replication/performance/secondaries`,
      'redirects to the secondaries page'
    );
    // nav back to details page and confirm secondary is in the known secondaries table
    await click('[data-test-replication-link="details"]');

    assert
      .dom(`[data-test-secondaries=row-for-${secondaryName}]`)
      .exists('shows a table row the recently added secondary');

    // nav to DR
    await visit('/vault/replication/dr');

    await fillIn('[data-test-replication-cluster-mode-select]', 'secondary');
    assert
      .dom('[data-test-replication-enable]')
      .isDisabled('dr secondary enable is disabled when other replication modes are on');

    // disable performance replication
    await disableReplication('performance', assert);
    await settled();
    await pollCluster(this.owner);

    // enable dr replication
    await visit('vault/replication/dr');

    await fillIn('[data-test-replication-cluster-mode-select]', 'primary');
    await click('button[type="submit"]');

    await pollCluster(this.owner);
    await waitUntil(() => find('[data-test-empty-state-title]'));
    // empty state inside of know secondaries table
    assert
      .dom('[data-test-empty-state-title]')
      .includesText(
        'No known dr secondary clusters associated with this cluster',
        'shows the correct title of the empty state'
      );

    assert.ok(
      find('[data-test-replication-title]').textContent.includes('Disaster Recovery'),
      'it displays the replication type correctly'
    );
    assert.ok(
      find('[data-test-replication-mode-display]').textContent.includes('primary'),
      'it displays the cluster mode correctly'
    );

    // add dr secondary
    await click('[data-test-replication-link="secondaries"]');

    await click('[data-test-secondary-add]');

    await fillIn('[data-test-replication-secondary-id]', secondaryName);

    await click('[data-test-secondary-add]');

    await pollCluster(this.owner);
    await click('[data-test-replication-link="secondaries"]');

    assert
      .dom('[data-test-secondary-name]')
      .includesText(secondaryName, 'it displays the secondary in the list of known secondaries');
  });

  test('disabling dr primary when perf replication is enabled', async function (assert) {
    await visit('vault/replication/performance');

    // enable perf replication
    await fillIn('[data-test-replication-cluster-mode-select]', 'primary');
    await click('[data-test-replication-enable]');

    await pollCluster(this.owner);

    // enable dr replication
    await visit('/vault/replication/dr');

    await fillIn('[data-test-replication-cluster-mode-select]', 'primary');

    await click('[data-test-replication-enable]');

    await pollCluster(this.owner);
    await visit('/vault/replication/dr/manage');

    await click('[data-test-demote-replication] [data-test-replication-action-trigger]');

    assert.ok(findAll('[data-test-demote-warning]').length, 'displays the demotion warning');
  });

  test('navigating to dr secondary details page when dr secondary is not enabled', async function (assert) {
    // enable dr replication

    await visit('/vault/replication/dr');

    await fillIn('[data-test-replication-cluster-mode-select]', 'primary');
    await click('[data-test-replication-enable]');
    await settled(); // eslint-disable-line
    await pollCluster(this.owner);
    await visit('/vault/replication-dr-promote/details');

    assert.dom('[data-test-component="empty-state"]').exists();
    assert
      .dom('[data-test-empty-state-message]')
      .hasText(
        'This Disaster Recovery secondary has not been enabled. You can do so from the Disaster Recovery Primary.',
        'renders message when replication is enabled'
      );
  });

  test('add secondary and navigate through token generation modal', async function (assert) {
    const secondaryNameFirst = 'firstSecondary';
    const secondaryNameSecond = 'secondSecondary';
    await visit('/vault/replication');

    // enable perf replication
    await click('[data-test-replication-type-select="performance"]');

    await fillIn('[data-test-replication-cluster-mode-select]', 'primary');
    await click('[data-test-replication-enable]');

    await pollCluster(this.owner);
    await settled();

    // add a secondary with default TTL
    await click('[data-test-replication-link="secondaries"]');

    await click('[data-test-secondary-add]');

    await fillIn('[data-test-replication-secondary-id]', secondaryNameFirst);
    await click('[data-test-secondary-add]');

    await pollCluster(this.owner);
    await settled();
    let modalDefaultTtl = document.querySelector('[data-test-row-value="TTL"]').innerText;
    // checks on secondary token modal
    assert.dom('#modal-wormhole').exists();
    assert.equal(modalDefaultTtl, '1800s', 'shows the correct TTL of 1800s');
    // click off the modal to make sure you don't just have to click on the copy-close button to copy the token
    await click('[data-test-modal-background]');

    // add another secondary not using the default ttl
    await click('[data-test-secondary-add]');

    await fillIn('[data-test-replication-secondary-id]', secondaryNameSecond);
    await click('[data-test-toggle-input]');

    await fillIn('[data-test-ttl-value]', 3);
    await click('[data-test-secondary-add]');

    await pollCluster(this.owner);
    await settled();
    let modalTtl = document.querySelector('[data-test-row-value="TTL"]').innerText;
    assert.equal(modalTtl, '180s', 'shows the correct TTL of 180s');
    await click('[data-test-modal-background]');

    // confirm you were redirected to the secondaries page
    assert.equal(
      currentURL(),
      `/vault/replication/performance/secondaries`,
      'redirects to the secondaries page'
    );
    assert
      .dom('[data-test-secondary-name]')
      .includesText(secondaryNameFirst, 'it displays the secondary in the list of secondaries');
  });

  test('render performance and dr primary and navigate to details page', async function (assert) {
    // enable perf primary replication
    await visit('/vault/replication');
    await click('[data-test-replication-type-select="performance"]');

    await fillIn('[data-test-replication-cluster-mode-select]', 'primary');
    await click('[data-test-replication-enable]');

    await pollCluster(this.owner);
    await settled();

    await visit('/vault/replication');

    assert
      .dom(`[data-test-replication-summary-card]`)
      .doesNotExist(`does not render replication summary card when both modes are not enabled as primary`);

    // enable DR primary replication
    const enableButton = document.querySelector('.is-primary');

    await click(enableButton);

    await click('[data-test-replication-enable]');

    await pollCluster(this.owner);
    await settled();

    // navigate using breadcrumbs back to replication.index
    await click('[data-test-replication-breadcrumb]');

    assert
      .dom('[data-test-replication-summary-card]')
      .exists({ count: 2 }, 'renders two replication-summary-card components');

    // navigate to details page using the "Details" link
    await click('[data-test-manage-link="Disaster Recovery"]');

    assert
      .dom('[data-test-selectable-card-container="primary"]')
      .exists('shows the correct card on the details dashboard');
    assert.equal(currentURL(), '/vault/replication/dr');
  });

  test('render performance secondary and navigate to the details page', async function (assert) {
    // enable perf replication
    await visit('/vault/replication');

    await click('[data-test-replication-type-select="performance"]');

    await fillIn('[data-test-replication-cluster-mode-select]', 'primary');
    await click('[data-test-replication-enable]');

    await pollCluster(this.owner);
    await settled();

    // demote perf primary to a secondary
    await click('[data-test-replication-link="manage"]');

    // open demote modal
    await click('[data-test-demote-replication] [data-test-replication-action-trigger]');

    // enter confirmation text
    await fillIn('[data-test-confirmation-modal-input="demote"]', 'Performance');
    // Click confirm button
    await click('[data-test-confirm-button="demote"]');

    await click('[data-test-replication-link="details"]');

    assert.dom('[data-test-replication-dashboard]').exists();
    assert.dom('[data-test-selectable-card-container="secondary"]').exists();
    assert.ok(
      find('[data-test-replication-mode-display]').textContent.includes('secondary'),
      'it displays the cluster mode correctly'
    );
  });
});
<commit_msg>Fix replication test failure (#16380)<commit_after>import { clickTrigger } from 'ember-power-select/test-support/helpers';
import { click, fillIn, findAll, currentURL, find, visit, settled, waitUntil } from '@ember/test-helpers';
import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import authPage from 'vault/tests/pages/auth';
import { pollCluster } from 'vault/tests/helpers/poll-cluster';
import { create } from 'ember-cli-page-object';
import flashMessage from 'vault/tests/pages/components/flash-message';
import ss from 'vault/tests/pages/components/search-select';

const searchSelect = create(ss);
const flash = create(flashMessage);

const disableReplication = async (type, assert) => {
  // disable performance replication
  await visit(`/vault/replication/${type}`);

  if (findAll('[data-test-replication-link="manage"]').length) {
    await click('[data-test-replication-link="manage"]');

    await click('[data-test-disable-replication] button');

    const typeDisplay = type === 'dr' ? 'Disaster Recovery' : 'Performance';
    await fillIn('[data-test-confirmation-modal-input="Disable Replication?"]', typeDisplay);
    await click('[data-test-confirm-button]');
    await settled(); // eslint-disable-line

    if (assert) {
      // bypassing for now -- remove if tests pass reliably
      // assert.equal(
      //   flash.latestMessage,
      //   'This cluster is having replication disabled. Vault will be unavailable for a brief period and will resume service shortly.',
      //   'renders info flash when disabled'
      // );
      assert.ok(
        await waitUntil(() => currentURL() === '/vault/replication'),
        'redirects to the replication page'
      );
    }
    await settled();
  }
};

module('Acceptance | Enterprise | replication', function (hooks) {
  setupApplicationTest(hooks);

  hooks.beforeEach(async function () {
    await authPage.login();
    await settled();
    await disableReplication('dr');
    await settled();
    await disableReplication('performance');
    await settled();
  });

  hooks.afterEach(async function () {
    await disableReplication('dr');
    await settled();
    await disableReplication('performance');
    await settled();
  });

  test('replication', async function (assert) {
    assert.expect(17);
    const secondaryName = 'firstSecondary';
    const mode = 'deny';
    let mountPath;

    // confirm unable to visit dr secondary details page when both replications are disabled
    await visit('/vault/replication-dr-promote/details');

    assert.dom('[data-test-component="empty-state"]').exists();
    assert
      .dom('[data-test-empty-state-title]')
      .includesText('Disaster Recovery secondary not set up', 'shows the correct title of the empty state');

    assert
      .dom('[data-test-empty-state-message]')
      .hasText(
        'This cluster has not been enabled as a Disaster Recovery Secondary. You can do so by enabling replication and adding a secondary from the Disaster Recovery Primary.',
        'renders default message specific to when no replication is enabled'
      );

    await visit('/vault/replication');

    assert.equal(currentURL(), '/vault/replication');

    // enable perf replication
    await click('[data-test-replication-type-select="performance"]');

    await fillIn('[data-test-replication-cluster-mode-select]', 'primary');

    await click('[data-test-replication-enable]');

    await pollCluster(this.owner);

    // confirm that the details dashboard shows
    assert.ok(await waitUntil(() => find('[data-test-replication-dashboard]')), 'details dashboard is shown');

    // add a secondary with a mount filter config
    await click('[data-test-replication-link="secondaries"]');

    await click('[data-test-secondary-add]');

    await fillIn('[data-test-replication-secondary-id]', secondaryName);

    await click('#deny');
    await clickTrigger();
    mountPath = searchSelect.options.objectAt(0).text;
    await searchSelect.options.objectAt(0).click();
    await click('[data-test-secondary-add]');

    await pollCluster(this.owner);
    // click into the added secondary's mount filter config
    await click('[data-test-replication-link="secondaries"]');

    await click('[data-test-popup-menu-trigger]');

    await click('[data-test-replication-path-filter-link]');

    assert.equal(currentURL(), `/vault/replication/performance/secondaries/config/show/${secondaryName}`);
    assert.dom('[data-test-mount-config-mode]').includesText(mode, 'show page renders the correct mode');
    assert
      .dom('[data-test-mount-config-paths]')
      .includesText(mountPath, 'show page renders the correct mount path');

    // delete config by choosing "no filter" in the edit screen
    await click('[data-test-replication-link="edit-mount-config"]');

    await click('#no-filtering');

    await click('[data-test-config-save]');
    await settled(); // eslint-disable-line

    assert.equal(
      flash.latestMessage,
      `The performance mount filter config for the secondary ${secondaryName} was successfully deleted.`,
      'renders success flash upon deletion'
    );
    assert.equal(
      currentURL(),
      `/vault/replication/performance/secondaries`,
      'redirects to the secondaries page'
    );
    // nav back to details page and confirm secondary is in the known secondaries table
    await click('[data-test-replication-link="details"]');

    assert
      .dom(`[data-test-secondaries=row-for-${secondaryName}]`)
      .exists('shows a table row the recently added secondary');

    // nav to DR
    await visit('/vault/replication/dr');

    await fillIn('[data-test-replication-cluster-mode-select]', 'secondary');
    assert
      .dom('[data-test-replication-enable]')
      .isDisabled('dr secondary enable is disabled when other replication modes are on');

    // disable performance replication
    await disableReplication('performance', assert);
    await settled();
    await pollCluster(this.owner);

    // enable dr replication
    await visit('vault/replication/dr');

    await fillIn('[data-test-replication-cluster-mode-select]', 'primary');
    await click('button[type="submit"]');

    await pollCluster(this.owner);
    await waitUntil(() => find('[data-test-empty-state-title]'));
    // empty state inside of know secondaries table
    assert
      .dom('[data-test-empty-state-title]')
      .includesText(
        'No known dr secondary clusters associated with this cluster',
        'shows the correct title of the empty state'
      );

    assert.ok(
      find('[data-test-replication-title]').textContent.includes('Disaster Recovery'),
      'it displays the replication type correctly'
    );
    assert.ok(
      find('[data-test-replication-mode-display]').textContent.includes('primary'),
      'it displays the cluster mode correctly'
    );

    // add dr secondary
    await click('[data-test-replication-link="secondaries"]');

    await click('[data-test-secondary-add]');

    await fillIn('[data-test-replication-secondary-id]', secondaryName);

    await click('[data-test-secondary-add]');

    await pollCluster(this.owner);
    await click('[data-test-replication-link="secondaries"]');

    assert
      .dom('[data-test-secondary-name]')
      .includesText(secondaryName, 'it displays the secondary in the list of known secondaries');
  });

  test('disabling dr primary when perf replication is enabled', async function (assert) {
    await visit('vault/replication/performance');

    // enable perf replication
    await fillIn('[data-test-replication-cluster-mode-select]', 'primary');
    await click('[data-test-replication-enable]');

    await pollCluster(this.owner);

    // enable dr replication
    await visit('/vault/replication/dr');

    await fillIn('[data-test-replication-cluster-mode-select]', 'primary');

    await click('[data-test-replication-enable]');

    await pollCluster(this.owner);
    await visit('/vault/replication/dr/manage');

    await click('[data-test-demote-replication] [data-test-replication-action-trigger]');

    assert.ok(findAll('[data-test-demote-warning]').length, 'displays the demotion warning');
  });

  test('navigating to dr secondary details page when dr secondary is not enabled', async function (assert) {
    // enable dr replication

    await visit('/vault/replication/dr');

    await fillIn('[data-test-replication-cluster-mode-select]', 'primary');
    await click('[data-test-replication-enable]');
    await settled(); // eslint-disable-line
    await pollCluster(this.owner);
    await visit('/vault/replication-dr-promote/details');

    assert.dom('[data-test-component="empty-state"]').exists();
    assert
      .dom('[data-test-empty-state-message]')
      .hasText(
        'This Disaster Recovery secondary has not been enabled. You can do so from the Disaster Recovery Primary.',
        'renders message when replication is enabled'
      );
  });

  test('add secondary and navigate through token generation modal', async function (assert) {
    const secondaryNameFirst = 'firstSecondary';
    const secondaryNameSecond = 'secondSecondary';
    await visit('/vault/replication');

    // enable perf replication
    await click('[data-test-replication-type-select="performance"]');

    await fillIn('[data-test-replication-cluster-mode-select]', 'primary');
    await click('[data-test-replication-enable]');

    await pollCluster(this.owner);
    await settled();

    // add a secondary with default TTL
    await click('[data-test-replication-link="secondaries"]');

    await click('[data-test-secondary-add]');

    await fillIn('[data-test-replication-secondary-id]', secondaryNameFirst);
    await click('[data-test-secondary-add]');

    await pollCluster(this.owner);
    await settled();
    let modalDefaultTtl = document.querySelector('[data-test-row-value="TTL"]').innerText;
    // checks on secondary token modal
    assert.dom('#modal-wormhole').exists();
    assert.equal(modalDefaultTtl, '1800s', 'shows the correct TTL of 1800s');
    // click off the modal to make sure you don't just have to click on the copy-close button to copy the token
    await click('[data-test-modal-background]');

    // add another secondary not using the default ttl
    await click('[data-test-secondary-add]');

    await fillIn('[data-test-replication-secondary-id]', secondaryNameSecond);
    await click('[data-test-toggle-input]');

    await fillIn('[data-test-ttl-value]', 3);
    await click('[data-test-secondary-add]');

    await pollCluster(this.owner);
    await settled();
    let modalTtl = document.querySelector('[data-test-row-value="TTL"]').innerText;
    assert.equal(modalTtl, '180s', 'shows the correct TTL of 180s');
    await click('[data-test-modal-background]');

    // confirm you were redirected to the secondaries page
    assert.equal(
      currentURL(),
      `/vault/replication/performance/secondaries`,
      'redirects to the secondaries page'
    );
    assert
      .dom('[data-test-secondary-name]')
      .includesText(secondaryNameFirst, 'it displays the secondary in the list of secondaries');
  });

  test('render performance and dr primary and navigate to details page', async function (assert) {
    // enable perf primary replication
    await visit('/vault/replication');
    await click('[data-test-replication-type-select="performance"]');

    await fillIn('[data-test-replication-cluster-mode-select]', 'primary');
    await click('[data-test-replication-enable]');

    await pollCluster(this.owner);
    await settled();

    await visit('/vault/replication');

    assert
      .dom(`[data-test-replication-summary-card]`)
      .doesNotExist(`does not render replication summary card when both modes are not enabled as primary`);

    // enable DR primary replication
    const enableButton = document.querySelector('.is-primary');

    await click(enableButton);

    await click('[data-test-replication-enable]');

    await pollCluster(this.owner);
    await settled();

    // navigate using breadcrumbs back to replication.index
    await click('[data-test-replication-breadcrumb]');

    assert
      .dom('[data-test-replication-summary-card]')
      .exists({ count: 2 }, 'renders two replication-summary-card components');

    // navigate to details page using the "Details" link
    await click('[data-test-manage-link="Disaster Recovery"]');

    assert
      .dom('[data-test-selectable-card-container="primary"]')
      .exists('shows the correct card on the details dashboard');
    assert.equal(currentURL(), '/vault/replication/dr');
  });

  test('render performance secondary and navigate to the details page', async function (assert) {
    // enable perf replication
    await visit('/vault/replication');

    await click('[data-test-replication-type-select="performance"]');

    await fillIn('[data-test-replication-cluster-mode-select]', 'primary');
    await click('[data-test-replication-enable]');

    await pollCluster(this.owner);
    await settled();

    // demote perf primary to a secondary
    await click('[data-test-replication-link="manage"]');

    // open demote modal
    await click('[data-test-demote-replication] [data-test-replication-action-trigger]');

    // enter confirmation text
    await fillIn('[data-test-confirmation-modal-input="Demote to secondary?"]', 'Performance');
    // Click confirm button
    await click('[data-test-confirm-button="Demote to secondary?"]');

    await click('[data-test-replication-link="details"]');

    assert.dom('[data-test-replication-dashboard]').exists();
    assert.dom('[data-test-selectable-card-container="secondary"]').exists();
    assert.ok(
      find('[data-test-replication-mode-display]').textContent.includes('secondary'),
      'it displays the cluster mode correctly'
    );
  });
});
<|endoftext|>


<commit_before>var assert = require('assert');
var fs = require('fs');
var rimraf = require('rimraf');
var imageUtils = require('./utils/image.js');
var childUtils = require('./utils/child.js');

// Clean up actual-files directory
before(function (done) {
  rimraf(__dirname + '/actual-files/', done);
});

describe('gulp.spritesmith', function () {
  describe('running a task without any options', function () {
    childUtils.run('gulp sprite-default');
    imageUtils.loadActual(__dirname + '/actual-files/default/sprite.png');
    imageUtils.loadExpected(__dirname + '/expected-files/default/mint-graphicsmagick.png');

    it('generates a top-down png', function () {
      assert.deepEqual(this.actualPixels, this.expectedPixels);
    });

    it('generates a css file', function () {
      var actualCss = fs.readFileSync(__dirname + '/actual-files/default/sprite.css', 'utf8');
      var expectedCss = fs.readFileSync(__dirname + '/expected-files/default/sprite.css', 'utf8');
      assert.strictEqual(actualCss, expectedCss);
    });
  });

  describe('returns "img" and "css" streams', function () {
    childUtils.run('gulp sprite-two-streams');
    imageUtils.loadActual(__dirname + '/actual-files/two-streams/sprite.png');
    imageUtils.loadExpected(__dirname + '/expected-files/two-streams/mint-graphicsmagick.png');

    it('generates a top-down png', function () {
      assert.deepEqual(this.actualPixels, this.expectedPixels);
    });

    it('generates a css file', function () {
      var actualCss = fs.readFileSync(__dirname + '/actual-files/two-streams/sprite.css', 'utf8');
      var expectedCss = fs.readFileSync(__dirname + '/expected-files/two-streams/sprite.css', 'utf8');
      assert.strictEqual(actualCss, expectedCss);
    });
  });

  describe('running a task with output formats', function () {
    childUtils.run('gulp sprite-formats');
    imageUtils.loadActual(__dirname + '/actual-files/formats/sprite.jpg');
    imageUtils.loadExpected(__dirname + '/expected-files/formats/mint-pngsmith.png');

    it('generates a top-down png (as a .jpg)', function () {
      assert.deepEqual(this.actualPixels, this.expectedPixels);
    });

    it('generates a Stylus file (as a .css)', function () {
      var actualCss = fs.readFileSync(__dirname + '/actual-files/formats/sprite.css', 'utf8');
      var expectedCss = fs.readFileSync(__dirname + '/expected-files/formats/sprite.styl', 'utf8');
      assert.strictEqual(actualCss, expectedCss);
    });
  });

  describe('running a task with engine and algorithm options', function () {
    childUtils.run('gulp sprite-options');
    imageUtils.loadActual(__dirname + '/actual-files/options/sprite.png');
    imageUtils.loadExpected(__dirname + '/expected-files/options/mint-pngsmith.png');

    it('generates an alt-diagonal png via the gm engine', function () {
      assert.deepEqual(this.actualPixels, this.expectedPixels);
    });

    it('generates a CSS file with alt-diagonal coordinates', function () {
      var actualCss = fs.readFileSync(__dirname + '/actual-files/options/sprite.css', 'utf8');
      var expectedCss = fs.readFileSync(__dirname + '/expected-files/options/sprite.css', 'utf8');
      assert.strictEqual(actualCss, expectedCss);
    });
  });

  describe('running a task with a custom CSS template', function () {
    childUtils.run('gulp sprite-template');
    imageUtils.loadActual(__dirname + '/actual-files/template/sprite.png');
    imageUtils.loadExpected(__dirname + '/expected-files/template/mint-graphicsmagick.png');

    it('generates a top-down png', function () {
      assert.deepEqual(this.actualPixels, this.expectedPixels);
    });

    it('generates a css file', function () {
      var actualCss = fs.readFileSync(__dirname + '/actual-files/template/sprite.scss', 'utf8');
      var expectedCss = fs.readFileSync(__dirname + '/expected-files/template/sprite.scss', 'utf8');
      assert.strictEqual(actualCss, expectedCss);
    });
  });

  // DEV: `gulp-newer` presents no input files when the task does not need to be run. See #17
  describe('running a task with no input images', function () {
    childUtils.run('gulp sprite-empty');
    imageUtils.loadActual(__dirname + '/actual-files/empty/sprite.png');
    imageUtils.loadExpected(__dirname + '/expected-files/empty/mint-graphicsmagick.png');

    it('generates a top-down png', function () {
      assert.deepEqual(this.actualPixels, this.expectedPixels);
    });

    it('generates a css file', function () {
      var actualCss = fs.readFileSync(__dirname + '/actual-files/template/sprite.scss', 'utf8');
      var expectedCss = fs.readFileSync(__dirname + '/expected-files/template/sprite.scss', 'utf8');
      assert.strictEqual(actualCss, expectedCss);
    });
  });
});
<commit_msg>Completed test but getting flak from gulp =/<commit_after>var assert = require('assert');
var fs = require('fs');
var rimraf = require('rimraf');
var imageUtils = require('./utils/image.js');
var childUtils = require('./utils/child.js');

// Clean up actual-files directory
before(function (done) {
  rimraf(__dirname + '/actual-files/', done);
});

describe('gulp.spritesmith', function () {
  describe('running a task without any options', function () {
    childUtils.run('gulp sprite-default');
    imageUtils.loadActual(__dirname + '/actual-files/default/sprite.png');
    imageUtils.loadExpected(__dirname + '/expected-files/default/mint-graphicsmagick.png');

    it('generates a top-down png', function () {
      assert.deepEqual(this.actualPixels, this.expectedPixels);
    });

    it('generates a css file', function () {
      var actualCss = fs.readFileSync(__dirname + '/actual-files/default/sprite.css', 'utf8');
      var expectedCss = fs.readFileSync(__dirname + '/expected-files/default/sprite.css', 'utf8');
      assert.strictEqual(actualCss, expectedCss);
    });
  });

  describe('returns "img" and "css" streams', function () {
    childUtils.run('gulp sprite-two-streams');
    imageUtils.loadActual(__dirname + '/actual-files/two-streams/sprite.png');
    imageUtils.loadExpected(__dirname + '/expected-files/two-streams/mint-graphicsmagick.png');

    it('generates a top-down png', function () {
      assert.deepEqual(this.actualPixels, this.expectedPixels);
    });

    it('generates a css file', function () {
      var actualCss = fs.readFileSync(__dirname + '/actual-files/two-streams/sprite.css', 'utf8');
      var expectedCss = fs.readFileSync(__dirname + '/expected-files/two-streams/sprite.css', 'utf8');
      assert.strictEqual(actualCss, expectedCss);
    });
  });

  describe('running a task with output formats', function () {
    childUtils.run('gulp sprite-formats');
    imageUtils.loadActual(__dirname + '/actual-files/formats/sprite.jpg');
    imageUtils.loadExpected(__dirname + '/expected-files/formats/mint-pngsmith.png');

    it('generates a top-down png (as a .jpg)', function () {
      assert.deepEqual(this.actualPixels, this.expectedPixels);
    });

    it('generates a Stylus file (as a .css)', function () {
      var actualCss = fs.readFileSync(__dirname + '/actual-files/formats/sprite.css', 'utf8');
      var expectedCss = fs.readFileSync(__dirname + '/expected-files/formats/sprite.styl', 'utf8');
      assert.strictEqual(actualCss, expectedCss);
    });
  });

  describe('running a task with engine and algorithm options', function () {
    childUtils.run('gulp sprite-options');
    imageUtils.loadActual(__dirname + '/actual-files/options/sprite.png');
    imageUtils.loadExpected(__dirname + '/expected-files/options/mint-pngsmith.png');

    it('generates an alt-diagonal png via the gm engine', function () {
      assert.deepEqual(this.actualPixels, this.expectedPixels);
    });

    it('generates a CSS file with alt-diagonal coordinates', function () {
      var actualCss = fs.readFileSync(__dirname + '/actual-files/options/sprite.css', 'utf8');
      var expectedCss = fs.readFileSync(__dirname + '/expected-files/options/sprite.css', 'utf8');
      assert.strictEqual(actualCss, expectedCss);
    });
  });

  describe('running a task with a custom CSS template', function () {
    childUtils.run('gulp sprite-template');
    imageUtils.loadActual(__dirname + '/actual-files/template/sprite.png');
    imageUtils.loadExpected(__dirname + '/expected-files/template/mint-graphicsmagick.png');

    it('generates a top-down png', function () {
      assert.deepEqual(this.actualPixels, this.expectedPixels);
    });

    it('generates a css file', function () {
      var actualCss = fs.readFileSync(__dirname + '/actual-files/template/sprite.scss', 'utf8');
      var expectedCss = fs.readFileSync(__dirname + '/expected-files/template/sprite.scss', 'utf8');
      assert.strictEqual(actualCss, expectedCss);
    });
  });

  // DEV: `gulp-newer` presents no input files when the task does not need to be run. See #17
  describe('running a task with no input images', function () {
    childUtils.run('gulp sprite-empty');

    it('does not generate a top-down png', function () {
      var imgExists = fs.existsSync(__dirname + '/actual-files/empty/sprite.png');
      assert.strictEqual(imgExists, false);
    });

    it('does not generate a css file', function () {
      var cssExists = fs.existsSync(__dirname + '/actual-files/template/sprite.scss');
      assert.strictEqual(cssExists, false);
    });
  });
});
<|endoftext|>


<commit_before>const { app } = require('electron');
const { BrowserWindow } = require('electron');
const { protocol } = require('electron');
const path = require('path');
const cProcess = require('child_process').spawn;
const portscanner = require('portscanner');
const { imageSize } = require('image-size');

let io, server, browserWindows, ipc, apiProcess, loadURL;
let appApi, menu, dialogApi, notification, tray, webContents;
let globalShortcut, shellApi, screen, clipboard, autoUpdater;
let commandLine, browserView;
let powerMonitor;
let splashScreen, hostHook;
let mainWindowId, nativeTheme;
let dock;
let launchFile;
let launchUrl;
let ignoreApiProcessClosed = false;

let manifestJsonFileName = 'electron.manifest.json';
let watchable = false;
if (app.commandLine.hasSwitch('manifest')) {
    manifestJsonFileName = app.commandLine.getSwitchValue('manifest');
};

if (app.commandLine.hasSwitch('watch')) {
    watchable = true;
};

let currentBinPath = path.join(__dirname.replace('app.asar', ''), 'bin');
let manifestJsonFilePath = path.join(currentBinPath, manifestJsonFileName);

// if watch is enabled lets change the path
if (watchable) {
    currentBinPath = path.join(__dirname, '../../'); // go to project directory
    manifestJsonFilePath = path.join(currentBinPath, manifestJsonFileName);
}

//  handle macOS events for opening the app with a file, etc
app.on('will-finish-launching', () => {
    app.on('open-file', (evt, file) => {
        evt.preventDefault();
        launchFile = file;
    })
    app.on('open-url', (evt, url) => {
        evt.preventDefault();
        launchUrl = url;
    })
});

app.on('before-quit-for-update', () => {
    ignoreApiProcessClosed = true;
});

const manifestJsonFile = require(manifestJsonFilePath);

if (manifestJsonFile.singleInstance || manifestJsonFile.aspCoreBackendPort) {
    const mainInstance = app.requestSingleInstanceLock();
    app.on('second-instance', (events, args = []) => {
        args.forEach(parameter => {
            const words = parameter.split('=');

            if(words.length > 1) {
                app.commandLine.appendSwitch(words[0].replace('--', ''), words[1]);
            } else {
                app.commandLine.appendSwitch(words[0].replace('--', ''));
            }
        });

        const windows = BrowserWindow.getAllWindows();
        if (windows.length) {
            if (windows[0].isMinimized()) {
                windows[0].restore();
            }
            windows[0].focus();
        }
    });

    if (!mainInstance) {
        app.quit();
    }
}

//Some flags need to be set before app is ready
if (manifestJsonFile.hasOwnProperty('cliFlags') && manifestJsonFile.cliFlags.length > 0) {
    manifestJsonFile.cliFlags.forEach(flag => {
        app.commandLine.appendSwitch(flag);
    });
}

app.on('ready', () => {

    // Fix ERR_UNKNOWN_URL_SCHEME using file protocol
    // https://github.com/electron/electron/issues/23757
    protocol.registerFileProtocol('file', (request, callback) => {
        const pathname = request.url.replace('file:///', '');
        callback(pathname);
    });

    if (isSplashScreenEnabled()) {
        startSplashScreen();
    }
    // Added default port as configurable for port restricted environments.
    let defaultElectronPort = 8000;
    if (manifestJsonFile.electronPort) {
        defaultElectronPort = (manifestJsonFile.electronPort)
    }
    // hostname needs to be localhost, otherwise Windows Firewall will be triggered.
    portscanner.findAPortNotInUse(defaultElectronPort, 65535, 'localhost', function (error, port) {
        console.log('Electron Socket IO Port: ' + port);
        startSocketApiBridge(port);
    });
});

app.on('quit', async (event, exitCode) => {
    await server.close();

    var detachedProcess = false;

    if (manifestJsonFile.hasOwnProperty('detachedProcess')) {
        detachedProcess = manifestJsonFile.detachedProcess;
    }

    if (!detachedProcess) {
        apiProcess.kill();
    }
});

function isSplashScreenEnabled() {
    if (manifestJsonFile.hasOwnProperty('splashscreen')) {
        if (manifestJsonFile.splashscreen.hasOwnProperty('imageFile')) {
            return Boolean(manifestJsonFile.splashscreen.imageFile);
        }
    }

    return false;
}

function startSplashScreen() {
    let imageFile = path.join(currentBinPath, manifestJsonFile.splashscreen.imageFile);
    imageSize(imageFile, (error, dimensions) => {
        if (error) {
            console.log('load splashscreen error:');
            console.error(error);

            throw new Error(error.message);
        }

        splashScreen = new BrowserWindow({
            width: dimensions.width,
            height: dimensions.height,
            transparent: true,
            center: true,
            frame: false,
            closable: false,
            resizable: false,
            skipTaskbar: true,
            alwaysOnTop: true,
            show: true
        });

        if (manifestJsonFile.hasOwnProperty('splashscreen')) {
            if (manifestJsonFile.splashscreen.hasOwnProperty('timeout')) {
                var timeout = manifestJsonFile.splashscreen.timeout;
                setTimeout((t) => {
                    if (splashScreen) {
                        splashScreen.hide();
                    }
                }, timeout);
            }
        }

        splashScreen.setIgnoreMouseEvents(true);

        app.once('browser-window-created', () => {
            splashScreen.destroy();
            splashScreen = null;
        });

        const loadSplashscreenUrl = path.join(__dirname, 'splashscreen', 'index.html') + '?imgPath=' + imageFile;

        splashScreen.loadURL('file://' + loadSplashscreenUrl);

        splashScreen.once('closed', () => {
            splashScreen = null;
        });
    });
}

function startSocketApiBridge(port) {

    // instead of 'require('socket.io')(port);' we need to use this workaround
    // otherwise the Windows Firewall will be triggered
    server = require('http').createServer();
    io = require('socket.io')();
    io.attach(server);

    server.listen(port, 'localhost');
    server.on('listening', function () {
        console.log('Electron Socket started on port %s at %s', server.address().port, server.address().address);
        // Now that socket connection is established, we can guarantee port will not be open for portscanner
        if (watchable) {
            startAspCoreBackendWithWatch(port);
        } else {
            startAspCoreBackend(port);
        }
    });

    // prototype
    app['mainWindowURL'] = "";
    app['mainWindow'] = null;

    // @ts-ignore
    io.on('connection', (socket) => {

        socket.on('disconnect', function (reason) {
            console.log('Got disconnect! Reason: ' + reason);
            try {
                if (hostHook) {
                    const hostHookScriptFilePath = path.join(__dirname, 'ElectronHostHook', 'index.js');
                    delete require.cache[require.resolve(hostHookScriptFilePath)];
                    hostHook = undefined;
                }

            } catch (error) {
                console.error(error.message);
            }
        });

        if (global['electronsocket'] === undefined) {
            global['electronsocket'] = socket;
            global['electronsocket'].setMaxListeners(0);
        }

        console.log('ASP.NET Core Application connected...', 'global.electronsocket', global['electronsocket'].id, new Date());

        if (appApi === undefined) appApi = require('./api/app')(socket, app);
        if (browserWindows === undefined) browserWindows = require('./api/browserWindows')(socket, app);
        if (commandLine === undefined) commandLine = require('./api/commandLine')(socket, app);
        if (autoUpdater === undefined) autoUpdater = require('./api/autoUpdater')(socket);
        if (ipc === undefined) ipc = require('./api/ipc')(socket);
        if (menu === undefined) menu = require('./api/menu')(socket);
        if (dialogApi === undefined) dialogApi = require('./api/dialog')(socket);
        if (notification === undefined) notification = require('./api/notification')(socket);
        if (tray === undefined) tray = require('./api/tray')(socket);
        if (webContents === undefined) webContents = require('./api/webContents')(socket);
        if (globalShortcut === undefined) globalShortcut = require('./api/globalShortcut')(socket);
        if (shellApi === undefined) shellApi = require('./api/shell')(socket);
        if (screen === undefined) screen = require('./api/screen')(socket);
        if (clipboard === undefined) clipboard = require('./api/clipboard')(socket);
        if (browserView === undefined) browserView = require('./api/browserView').browserViewApi(socket);
        if (powerMonitor === undefined) powerMonitor = require('./api/powerMonitor')(socket);
        if (nativeTheme === undefined) nativeTheme = require('./api/nativeTheme')(socket);
        if (dock === undefined) dock = require('./api/dock')(socket);

        socket.on('register-app-open-file-event', (id) => {
            global['electronsocket'] = socket;

            app.on('open-file', (event, file) => {
                event.preventDefault();

                global['electronsocket'].emit('app-open-file' + id, file);
            });

            if (launchFile) {
                global['electronsocket'].emit('app-open-file' + id, launchFile);
            }
        });

        socket.on('register-app-open-url-event', (id) => {
            global['electronsocket'] = socket;

            app.on('open-url', (event, url) => {
                event.preventDefault();

                global['electronsocket'].emit('app-open-url' + id, url);
            });

            if (launchUrl) {
                global['electronsocket'].emit('app-open-url' + id, launchUrl);
            }
        });

        socket.on('console-stdout', (data) => {
            console.log(`stdout: ${data.toString()}`);
        });

        socket.on('console-stderr', (data) => {
            console.log(`stderr: ${data.toString()}`);
        });

        try {
            const hostHookScriptFilePath = path.join(__dirname, 'ElectronHostHook', 'index.js');

            if (isModuleAvailable(hostHookScriptFilePath) && hostHook === undefined) {
                const { HookService } = require(hostHookScriptFilePath);
                hostHook = new HookService(socket, app);
                hostHook.onHostReady();
            }
        } catch (error) {
            console.error(error.message);
        }
    });
}

function isModuleAvailable(name) {
    try {
        require.resolve(name);
        return true;
    } catch (e) { }
    return false;
}

function startAspCoreBackend(electronPort) {
    if (manifestJsonFile.aspCoreBackendPort) {
        startBackend(manifestJsonFile.aspCoreBackendPort)
    } else {
        // hostname needs to be localhost, otherwise Windows Firewall will be triggered.
        portscanner.findAPortNotInUse(electronPort + 1, 65535, 'localhost', function (error, electronWebPort) {
            startBackend(electronWebPort);
        });
    }

    function startBackend(aspCoreBackendPort) {
        console.log('ASP.NET Core Port: ' + aspCoreBackendPort);
        loadURL = `http://localhost:${aspCoreBackendPort}`;
        const parameters = [getEnvironmentParameter(), `/electronPort=${electronPort}`, `/electronWebPort=${aspCoreBackendPort}`, `/electronPID=${process.pid}`];
        let binaryFile = manifestJsonFile.executable;

        const os = require('os');
        if (os.platform() === 'win32') {
            binaryFile = binaryFile + '.exe';
        }

        var detachedProcess = false;
        var stdioopt = 'pipe';

        if (manifestJsonFile.hasOwnProperty('detachedProcess')) {
            detachedProcess = manifestJsonFile.detachedProcess;
            if (detachedProcess) {
                stdioopt = 'ignore';
            }
        }

        let binFilePath = path.join(currentBinPath, binaryFile);

        var options = { cwd: currentBinPath, detached: detachedProcess, stdio: stdioopt  };

        apiProcess = cProcess(binFilePath, parameters, options);

        if (!detachedProcess) {
            apiProcess.stdout.on('data', (data) => {
                console.log(`stdout: ${data.toString()}`);
            });

            apiProcess.stderr.on('data', (data) => {
                console.log(`stderr: ${data.toString()}`);
            });
        }

        apiProcess.on('close', (code) => {
            console.log(`ASP.NET Process exited with code ${code}`);
            if (code != 0 && !ignoreApiProcessClosed) {
                console.log(`Will quit Electron, as exit code != 0 (got ${code})`);
                app.exit(code);
            }
        });

        if (detachedProcess) {
            console.log('Detached from ASP.NET process');
            apiProcess.unref();
        }
    }
}

function startAspCoreBackendWithWatch(electronPort) {
    if (manifestJsonFile.aspCoreBackendPort) {
        startBackend(manifestJsonFile.aspCoreBackendPort)
    } else {
        // hostname needs to be localhost, otherwise Windows Firewall will be triggered.
        portscanner.findAPortNotInUse(electronPort + 1, 65535, 'localhost', function (error, electronWebPort) {
            startBackend(electronWebPort);
        });
    }

    function startBackend(aspCoreBackendPort) {
        console.log('ASP.NET Core Watch Port: ' + aspCoreBackendPort);
        loadURL = `http://localhost:${aspCoreBackendPort}`;
        const parameters = ['watch', 'run', getEnvironmentParameter(), `/electronPort=${electronPort}`, `/electronWebPort=${aspCoreBackendPort}`, `/electronPID=${process.pid}`];

        var detachedProcess = false;
        var stdioopt = 'pipe';

        if (manifestJsonFile.hasOwnProperty('detachedProcess')) {
            detachedProcess = manifestJsonFile.detachedProcess;
            if (detachedProcess) {
                stdioopt = 'ignore';
            }
        }

        var options = { cwd: currentBinPath, env: process.env, detached: detachedProcess, stdio: stdioopt };

        apiProcess = cProcess('dotnet', parameters, options);

        if (!detachedProcess) {
            apiProcess.stdout.on('data', (data) => {
                console.log(`stdout: ${data.toString()}`);
            });

            apiProcess.stderr.on('data', (data) => {
                console.log(`stderr: ${data.toString()}`);
            });
        }

        apiProcess.on('close', (code) => {
            console.log(`ASP.NET Process exited with code ${code}`);
            if (code != 0 && !ignoreApiProcessClosed) {
                console.log(`Will quit Electron, as exit code != 0 (got ${code})`);
                app.exit(code);
            }
        });

        if (detachedProcess) {
            console.log('Detached from ASP.NET process');
            apiProcess.unref();
        }
    }
}

function getEnvironmentParameter() {
    if (manifestJsonFile.environment) {
        return '--environment=' + manifestJsonFile.environment;
    }

    return '';
}
<commit_msg>Workaround for electron freeze bug<commit_after>const { app } = require('electron');
const { BrowserWindow } = require('electron');
const { protocol } = require('electron');
const path = require('path');
const cProcess = require('child_process').spawn;
const portscanner = require('portscanner');
const { imageSize } = require('image-size');

let io, server, browserWindows, ipc, apiProcess, loadURL;
let appApi, menu, dialogApi, notification, tray, webContents;
let globalShortcut, shellApi, screen, clipboard, autoUpdater;
let commandLine, browserView;
let powerMonitor;
let splashScreen, hostHook;
let mainWindowId, nativeTheme;
let dock;
let launchFile;
let launchUrl;
let ignoreApiProcessClosed = false;

let manifestJsonFileName = 'electron.manifest.json';
let watchable = false;
if (app.commandLine.hasSwitch('manifest')) {
    manifestJsonFileName = app.commandLine.getSwitchValue('manifest');
};

if (app.commandLine.hasSwitch('watch')) {
    watchable = true;
};

let currentBinPath = path.join(__dirname.replace('app.asar', ''), 'bin');
let manifestJsonFilePath = path.join(currentBinPath, manifestJsonFileName);

// if watch is enabled lets change the path
if (watchable) {
    currentBinPath = path.join(__dirname, '../../'); // go to project directory
    manifestJsonFilePath = path.join(currentBinPath, manifestJsonFileName);
}

//  handle macOS events for opening the app with a file, etc
app.on('will-finish-launching', () => {
    app.on('open-file', (evt, file) => {
        evt.preventDefault();
        launchFile = file;
    })
    app.on('open-url', (evt, url) => {
        evt.preventDefault();
        launchUrl = url;
    })
});

app.on('before-quit-for-update', () => {
    ignoreApiProcessClosed = true;
});

const manifestJsonFile = require(manifestJsonFilePath);

if (manifestJsonFile.singleInstance || manifestJsonFile.aspCoreBackendPort) {
    const mainInstance = app.requestSingleInstanceLock();
    app.on('second-instance', (events, args = []) => {
        args.forEach(parameter => {
            const words = parameter.split('=');

            if(words.length > 1) {
                app.commandLine.appendSwitch(words[0].replace('--', ''), words[1]);
            } else {
                app.commandLine.appendSwitch(words[0].replace('--', ''));
            }
        });

        const windows = BrowserWindow.getAllWindows();
        if (windows.length) {
            if (windows[0].isMinimized()) {
                windows[0].restore();
            }
            windows[0].focus();
        }
    });

    if (!mainInstance) {
        app.quit();
    }
}

//Some flags need to be set before app is ready
if (manifestJsonFile.hasOwnProperty('cliFlags') && manifestJsonFile.cliFlags.length > 0) {
    manifestJsonFile.cliFlags.forEach(flag => {
        app.commandLine.appendSwitch(flag);
    });
}

app.on('ready', () => {

    // Fix ERR_UNKNOWN_URL_SCHEME using file protocol
    // https://github.com/electron/electron/issues/23757
    protocol.registerFileProtocol('file', (request, callback) => {
        const pathname = request.url.replace('file:///', '');
        callback(pathname);
    });

    if (isSplashScreenEnabled()) {
        startSplashScreen();
    }
    // Added default port as configurable for port restricted environments.
    let defaultElectronPort = 8000;
    if (manifestJsonFile.electronPort) {
        defaultElectronPort = (manifestJsonFile.electronPort)
    }
    // hostname needs to be localhost, otherwise Windows Firewall will be triggered.
    portscanner.findAPortNotInUse(defaultElectronPort, 65535, 'localhost', function (error, port) {
        console.log('Electron Socket IO Port: ' + port);
        startSocketApiBridge(port);
    });
});

app.on('quit', async (event, exitCode) => {
    await server.close();

    var detachedProcess = false;

    if (manifestJsonFile.hasOwnProperty('detachedProcess')) {
        detachedProcess = manifestJsonFile.detachedProcess;
    }

    if (!detachedProcess) {
        apiProcess.kill();
    }
});

function isSplashScreenEnabled() {
    if (manifestJsonFile.hasOwnProperty('splashscreen')) {
        if (manifestJsonFile.splashscreen.hasOwnProperty('imageFile')) {
            return Boolean(manifestJsonFile.splashscreen.imageFile);
        }
    }

    return false;
}

function startSplashScreen() {
    let imageFile = path.join(currentBinPath, manifestJsonFile.splashscreen.imageFile);
    imageSize(imageFile, (error, dimensions) => {
        if (error) {
            console.log('load splashscreen error:');
            console.error(error);

            throw new Error(error.message);
        }

        splashScreen = new BrowserWindow({
            width: dimensions.width,
            height: dimensions.height,
            transparent: true,
            center: true,
            frame: false,
            closable: false,
            resizable: false,
            skipTaskbar: true,
            alwaysOnTop: true,
            show: true
        });

        if (manifestJsonFile.hasOwnProperty('splashscreen')) {
            if (manifestJsonFile.splashscreen.hasOwnProperty('timeout')) {
                var timeout = manifestJsonFile.splashscreen.timeout;
                setTimeout((t) => {
                    if (splashScreen) {
                        splashScreen.hide();
                    }
                }, timeout);
            }
        }

        //Removed as we want to be able to drag the splash screen: splashScreen.setIgnoreMouseEvents(true);

        app.once('browser-window-created', () => {
            if (splashScreen) {
                splashScreen.hide();
            }
            //We cannot destroy the window here as this triggers an electron freeze bug (https://github.com/electron/electron/issues/29050)
        });

        const loadSplashscreenUrl = path.join(__dirname, 'splashscreen', 'index.html') + '?imgPath=' + imageFile;

        splashScreen.loadURL('file://' + loadSplashscreenUrl);

        splashScreen.once('closed', () => {
            splashScreen = null;
        });
    });
}

function startSocketApiBridge(port) {

    // instead of 'require('socket.io')(port);' we need to use this workaround
    // otherwise the Windows Firewall will be triggered
    server = require('http').createServer();
    io = require('socket.io')();
    io.attach(server);

    server.listen(port, 'localhost');
    server.on('listening', function () {
        console.log('Electron Socket started on port %s at %s', server.address().port, server.address().address);
        // Now that socket connection is established, we can guarantee port will not be open for portscanner
        if (watchable) {
            startAspCoreBackendWithWatch(port);
        } else {
            startAspCoreBackend(port);
        }
    });

    // prototype
    app['mainWindowURL'] = "";
    app['mainWindow'] = null;

    // @ts-ignore
    io.on('connection', (socket) => {

        socket.on('disconnect', function (reason) {
            console.log('Got disconnect! Reason: ' + reason);
            try {
                if (hostHook) {
                    const hostHookScriptFilePath = path.join(__dirname, 'ElectronHostHook', 'index.js');
                    delete require.cache[require.resolve(hostHookScriptFilePath)];
                    hostHook = undefined;
                }

            } catch (error) {
                console.error(error.message);
            }
        });

        if (global['electronsocket'] === undefined) {
            global['electronsocket'] = socket;
            global['electronsocket'].setMaxListeners(0);
        }

        console.log('ASP.NET Core Application connected...', 'global.electronsocket', global['electronsocket'].id, new Date());

        if (appApi === undefined) appApi = require('./api/app')(socket, app);
        if (browserWindows === undefined) browserWindows = require('./api/browserWindows')(socket, app);
        if (commandLine === undefined) commandLine = require('./api/commandLine')(socket, app);
        if (autoUpdater === undefined) autoUpdater = require('./api/autoUpdater')(socket);
        if (ipc === undefined) ipc = require('./api/ipc')(socket);
        if (menu === undefined) menu = require('./api/menu')(socket);
        if (dialogApi === undefined) dialogApi = require('./api/dialog')(socket);
        if (notification === undefined) notification = require('./api/notification')(socket);
        if (tray === undefined) tray = require('./api/tray')(socket);
        if (webContents === undefined) webContents = require('./api/webContents')(socket);
        if (globalShortcut === undefined) globalShortcut = require('./api/globalShortcut')(socket);
        if (shellApi === undefined) shellApi = require('./api/shell')(socket);
        if (screen === undefined) screen = require('./api/screen')(socket);
        if (clipboard === undefined) clipboard = require('./api/clipboard')(socket);
        if (browserView === undefined) browserView = require('./api/browserView').browserViewApi(socket);
        if (powerMonitor === undefined) powerMonitor = require('./api/powerMonitor')(socket);
        if (nativeTheme === undefined) nativeTheme = require('./api/nativeTheme')(socket);
        if (dock === undefined) dock = require('./api/dock')(socket);

        socket.on('register-app-open-file-event', (id) => {
            global['electronsocket'] = socket;

            app.on('open-file', (event, file) => {
                event.preventDefault();

                global['electronsocket'].emit('app-open-file' + id, file);
            });

            if (launchFile) {
                global['electronsocket'].emit('app-open-file' + id, launchFile);
            }
        });

        socket.on('splashscreen-destroy', () => {
            if(splashScreen) {
                splashScreen.destroy();
                splashScreen = null;
            }
        });

        socket.on('register-app-open-url-event', (id) => {
            global['electronsocket'] = socket;

            app.on('open-url', (event, url) => {
                event.preventDefault();

                global['electronsocket'].emit('app-open-url' + id, url);
            });

            if (launchUrl) {
                global['electronsocket'].emit('app-open-url' + id, launchUrl);
            }
        });

        socket.on('console-stdout', (data) => {
            console.log(`stdout: ${data.toString()}`);
        });

        socket.on('console-stderr', (data) => {
            console.log(`stderr: ${data.toString()}`);
        });

        try {
            const hostHookScriptFilePath = path.join(__dirname, 'ElectronHostHook', 'index.js');

            if (isModuleAvailable(hostHookScriptFilePath) && hostHook === undefined) {
                const { HookService } = require(hostHookScriptFilePath);
                hostHook = new HookService(socket, app);
                hostHook.onHostReady();
            }
        } catch (error) {
            console.error(error.message);
        }
    });
}

function isModuleAvailable(name) {
    try {
        require.resolve(name);
        return true;
    } catch (e) { }
    return false;
}

function startAspCoreBackend(electronPort) {
    if (manifestJsonFile.aspCoreBackendPort) {
        startBackend(manifestJsonFile.aspCoreBackendPort)
    } else {
        // hostname needs to be localhost, otherwise Windows Firewall will be triggered.
        portscanner.findAPortNotInUse(electronPort + 1, 65535, 'localhost', function (error, electronWebPort) {
            startBackend(electronWebPort);
        });
    }

    function startBackend(aspCoreBackendPort) {
        console.log('ASP.NET Core Port: ' + aspCoreBackendPort);
        loadURL = `http://localhost:${aspCoreBackendPort}`;
        const parameters = [getEnvironmentParameter(), `/electronPort=${electronPort}`, `/electronWebPort=${aspCoreBackendPort}`, `/electronPID=${process.pid}`];
        let binaryFile = manifestJsonFile.executable;

        const os = require('os');
        if (os.platform() === 'win32') {
            binaryFile = binaryFile + '.exe';
        }

        var detachedProcess = false;
        var stdioopt = 'pipe';

        if (manifestJsonFile.hasOwnProperty('detachedProcess')) {
            detachedProcess = manifestJsonFile.detachedProcess;
            if (detachedProcess) {
                stdioopt = 'ignore';
            }
        }

        let binFilePath = path.join(currentBinPath, binaryFile);

        var options = { cwd: currentBinPath, detached: detachedProcess, stdio: stdioopt  };

        apiProcess = cProcess(binFilePath, parameters, options);

        if (!detachedProcess) {
            apiProcess.stdout.on('data', (data) => {
                console.log(`stdout: ${data.toString()}`);
            });

            apiProcess.stderr.on('data', (data) => {
                console.log(`stderr: ${data.toString()}`);
            });
        }

        apiProcess.on('close', (code) => {
            console.log(`ASP.NET Process exited with code ${code}`);
            if (code != 0 && !ignoreApiProcessClosed) {
                console.log(`Will quit Electron, as exit code != 0 (got ${code})`);
                app.exit(code);
            }
        });

        if (detachedProcess) {
            console.log('Detached from ASP.NET process');
            apiProcess.unref();
        }
    }
}

function startAspCoreBackendWithWatch(electronPort) {
    if (manifestJsonFile.aspCoreBackendPort) {
        startBackend(manifestJsonFile.aspCoreBackendPort)
    } else {
        // hostname needs to be localhost, otherwise Windows Firewall will be triggered.
        portscanner.findAPortNotInUse(electronPort + 1, 65535, 'localhost', function (error, electronWebPort) {
            startBackend(electronWebPort);
        });
    }

    function startBackend(aspCoreBackendPort) {
        console.log('ASP.NET Core Watch Port: ' + aspCoreBackendPort);
        loadURL = `http://localhost:${aspCoreBackendPort}`;
        const parameters = ['watch', 'run', getEnvironmentParameter(), `/electronPort=${electronPort}`, `/electronWebPort=${aspCoreBackendPort}`, `/electronPID=${process.pid}`];

        var detachedProcess = false;
        var stdioopt = 'pipe';

        if (manifestJsonFile.hasOwnProperty('detachedProcess')) {
            detachedProcess = manifestJsonFile.detachedProcess;
            if (detachedProcess) {
                stdioopt = 'ignore';
            }
        }

        var options = { cwd: currentBinPath, env: process.env, detached: detachedProcess, stdio: stdioopt };

        apiProcess = cProcess('dotnet', parameters, options);

        if (!detachedProcess) {
            apiProcess.stdout.on('data', (data) => {
                console.log(`stdout: ${data.toString()}`);
            });

            apiProcess.stderr.on('data', (data) => {
                console.log(`stderr: ${data.toString()}`);
            });
        }

        apiProcess.on('close', (code) => {
            console.log(`ASP.NET Process exited with code ${code}`);
            if (code != 0 && !ignoreApiProcessClosed) {
                console.log(`Will quit Electron, as exit code != 0 (got ${code})`);
                app.exit(code);
            }
        });

        if (detachedProcess) {
            console.log('Detached from ASP.NET process');
            apiProcess.unref();
        }
    }
}

function getEnvironmentParameter() {
    if (manifestJsonFile.environment) {
        return '--environment=' + manifestJsonFile.environment;
    }

    return '';
}
<|endoftext|>