<commit_before>var buster = require("buster");
var args = require("./../lib/posix-argv-parser");

buster.testCase("posix-argv-parser", {
    setUp: function () {
        this.a = args.create();
    },

    "not passing any options": function () {
        assert.exception(function () {
            this.a.createOption([]);
        }.bind(this));
    },

    "handling non-existent option errors": function (done) {
        this.a.createOption(["-p"]);
        this.a.parse(["-z"], done(function (errors) {
            assert.equals(errors.length, 1);
            assert.match(errors[0], /unknown argument/i);
            assert.match(errors[0], "-z");
        }));
    },

    "one and two dash option with both passed, single first": function (done) {
        this.a.createOption(["-p"]);
        this.a.createOption(["--port"]);

        this.a.parse(["-p", "--port"], done(function (errors, options) {
            assert(options["-p"].isSet);
            assert.equals(options["-p"].timesSet, 1);
            assert(options["--port"].isSet);
            assert.equals(options["--port"].timesSet, 1);
        }));
    },

    "one and two dash option with both passed, double first": function (done) {
        this.a.createOption(["-p"]);
        this.a.createOption(["--port"]);

        this.a.parse(["--port", "-p"], done(function (errors, options) {
            assert(options["-p"].isSet);
            assert.equals(options["-p"].timesSet, 1);
            assert(options["--port"].isSet);
            assert.equals(options["--port"].timesSet, 1);
        }));
    },

    "one and two dash option with only double dash passed": function (done) {
        this.a.createOption(["-p"]);
        this.a.createOption(["--port"]);

        this.a.parse(["--port"], done(function (errors, options) {
            refute(options["-p"].isSet);
            assert(options["--port"].isSet);
            assert.equals(options["--port"].timesSet, 1);
        }));
    },

    "one and two dash option with only single dash passed": function (done) {
        this.a.createOption(["-p"]);
        this.a.createOption(["--port"]);

        this.a.parse(["-p"], done(function (errors, options) {
            assert(options["-p"].isSet);
            assert.equals(options["-p"].timesSet, 1);
            refute(options["--port"].isSet);
        }));
    },

    "same option specified twice in one option": function () {
        assert.exception(function () {
            this.a.createOption(["-p", "-p"]);
        }.bind(this));

        assert.exception(function () {
            this.a.createOption(["--port", "--port"]);
        }.bind(this));
    },

    "same option specified in a different option": function () {
        this.a.createOption(["-p"]);

        assert.exception(function () {
            this.a.createOption(["-p"]);
        }.bind(this));

        this.a.createOption(["--port"]);

        assert.exception(function () {
            this.a.createOption(["--port"]);
        }.bind(this));
    },

    "after operand separator": function (done) {
        this.a.createOption(["--port"]);

        this.a.parse(["--", "--port"], done(function (errors, options) {
            assert.defined(errors);
        }));
    },

    "yields options to parse callback": function (done) {
        this.a.createOption(["--port", "-p"], { hasValue: true });
        this.a.createOption(["--help"]);

        this.a.parse(["--port", "4210"], done(function (err, options) {
            assert.match(options["--port"], { value: "4210" });
            assert.match(options["-p"], { value: "4210" });
            assert.isFalse(options["--help"].isSet);
        }));
    },

    "yields options with named operand": function (done) {
        this.a.createOperand("filter");

        this.a.parse(["yay"], done(function (err, options) {
            assert.equals(options.filter.value, "yay");
        }));
    },

    "yields greedy operand value as array": function (done) {
        this.a.createOperand("filter", { greedy: true });

        this.a.parse(["yay", "man"], done(function (err, options) {
            assert.equals(options.filter.value, ["yay", "man"]);
        }));
    },

    "transforms": {
        "transforms value": function (done) {
            var transform = this.stub().returns(1337);
            this.a.createOption(["-p"], {
                hasValue: true,
                transform: transform
            });

            this.a.parse(["-p", "1337"], done(function (err, options) {
                assert.calledOnce(transform);
                assert.same(options["-p"].value, 1337);
            }));
        },

        "fails if transform is not a function": function () {
            assert.exception(function () {
                this.a.createOption(["-p"], {
                    hasValue: true,
                    transform: {}
                });
            }.bind(this));
        },

        "fails if transform throws": function (done) {
            this.a.createOption(["-p"], {
                hasValue: true,
                transform: this.stub().throws(new TypeError("Oh no"))
            });

            this.a.parse(["-p", "1337"], done(function (errors, options) {
                assert(errors);
                assert.match(errors[0], "Oh no");
            }));
        },

        "validates raw untransformed value": function (done) {
            var validatorValue;
            this.a.createOption(["-p"], {
                hasValue: true,
                validators: [function (opt) { validatorValue = opt.value; }],
                transform: this.stub().returns(1337)
            });

            this.a.parse(["-p", "AAA"], done(function (errors, options) {
                assert.equals(validatorValue, "AAA");
            }));
        },

        "does not call transform if validation fails": function (done) {
            var transform = this.stub();
            this.a.createOption(["-p"], {
                hasValue: true,
                validators: [args.validators.integer()],
                transform: transform
            });

            this.a.parse(["-p", "AAA"], done(function (errors, options) {
                assert(errors);
                refute.called(transform);
            }));
        }
    }
});
<commit_msg>Make sure non-provided options are in the results<commit_after>var buster = require("buster");
var args = require("./../lib/posix-argv-parser");

buster.testCase("posix-argv-parser", {
    setUp: function () {
        this.a = args.create();
    },

    "not passing any options": function () {
        assert.exception(function () {
            this.a.createOption([]);
        }.bind(this));
    },

    "handling non-existent option errors": function (done) {
        this.a.createOption(["-p"]);
        this.a.parse(["-z"], done(function (errors) {
            assert.equals(errors.length, 1);
            assert.match(errors[0], /unknown argument/i);
            assert.match(errors[0], "-z");
        }));
    },

    "one and two dash option with both passed, single first": function (done) {
        this.a.createOption(["-p"]);
        this.a.createOption(["--port"]);

        this.a.parse(["-p", "--port"], done(function (errors, options) {
            assert(options["-p"].isSet);
            assert.equals(options["-p"].timesSet, 1);
            assert(options["--port"].isSet);
            assert.equals(options["--port"].timesSet, 1);
        }));
    },

    "one and two dash option with both passed, double first": function (done) {
        this.a.createOption(["-p"]);
        this.a.createOption(["--port"]);

        this.a.parse(["--port", "-p"], done(function (errors, options) {
            assert(options["-p"].isSet);
            assert.equals(options["-p"].timesSet, 1);
            assert(options["--port"].isSet);
            assert.equals(options["--port"].timesSet, 1);
        }));
    },

    "one and two dash option with only double dash passed": function (done) {
        this.a.createOption(["-p"]);
        this.a.createOption(["--port"]);

        this.a.parse(["--port"], done(function (errors, options) {
            refute(options["-p"].isSet);
            assert(options["--port"].isSet);
            assert.equals(options["--port"].timesSet, 1);
        }));
    },

    "one and two dash option with only single dash passed": function (done) {
        this.a.createOption(["-p"]);
        this.a.createOption(["--port"]);

        this.a.parse(["-p"], done(function (errors, options) {
            assert(options["-p"].isSet);
            assert.equals(options["-p"].timesSet, 1);
            refute(options["--port"].isSet);
        }));
    },

    "same option specified twice in one option": function () {
        assert.exception(function () {
            this.a.createOption(["-p", "-p"]);
        }.bind(this));

        assert.exception(function () {
            this.a.createOption(["--port", "--port"]);
        }.bind(this));
    },

    "same option specified in a different option": function () {
        this.a.createOption(["-p"]);

        assert.exception(function () {
            this.a.createOption(["-p"]);
        }.bind(this));

        this.a.createOption(["--port"]);

        assert.exception(function () {
            this.a.createOption(["--port"]);
        }.bind(this));
    },

    "after operand separator": function (done) {
        this.a.createOption(["--port"]);

        this.a.parse(["--", "--port"], done(function (errors, options) {
            assert.defined(errors);
        }));
    },

    "yields options to parse callback": function (done) {
        this.a.createOption(["--port", "-p"], { hasValue: true });
        this.a.createOption(["--help"]);

        this.a.parse(["--port", "4210"], done(function (err, options) {
            assert.match(options["--port"], { value: "4210" });
            assert.match(options["-p"], { value: "4210" });
            assert.isFalse(options["--help"].isSet);
        }));
    },

    "yields options with named operand": function (done) {
        this.a.createOperand("filter");

        this.a.parse(["yay"], done(function (err, options) {
            assert.equals(options.filter.value, "yay");
        }));
    },

    "yields greedy operand value as array": function (done) {
        this.a.createOperand("filter", { greedy: true });

        this.a.parse(["yay", "man"], done(function (err, options) {
            assert.equals(options.filter.value, ["yay", "man"]);
        }));
    },

    "transforms": {
        "transforms value": function (done) {
            var transform = this.stub().returns(1337);
            this.a.createOption(["-p"], {
                hasValue: true,
                transform: transform
            });

            this.a.parse(["-p", "1337"], done(function (err, options) {
                assert.calledOnce(transform);
                assert.same(options["-p"].value, 1337);
            }));
        },

        "fails if transform is not a function": function () {
            assert.exception(function () {
                this.a.createOption(["-p"], {
                    hasValue: true,
                    transform: {}
                });
            }.bind(this));
        },

        "fails if transform throws": function (done) {
            this.a.createOption(["-p"], {
                hasValue: true,
                transform: this.stub().throws(new TypeError("Oh no"))
            });

            this.a.parse(["-p", "1337"], done(function (errors, options) {
                assert(errors);
                assert.match(errors[0], "Oh no");
            }));
        },

        "validates raw untransformed value": function (done) {
            var validatorValue;
            this.a.createOption(["-p"], {
                hasValue: true,
                validators: [function (opt) { validatorValue = opt.value; }],
                transform: this.stub().returns(1337)
            });

            this.a.parse(["-p", "AAA"], done(function (errors, options) {
                assert.equals(validatorValue, "AAA");
            }));
        },

        "does not call transform if validation fails": function (done) {
            var transform = this.stub();
            this.a.createOption(["-p"], {
                hasValue: true,
                validators: [args.validators.integer()],
                transform: transform
            });

            this.a.parse(["-p", "AAA"], done(function (errors, options) {
                assert(errors);
                refute.called(transform);
            }));
        }
    },

    "includes non-provided arguments in results": function (done) {
        this.a.createOption(["-p"]);
        this.a.parse([], done(function (errors, options) {
            assert.isFalse(options["-p"].isSet);
        }));
    }
});
<|endoftext|>


<commit_before>const {knex} = require('../util/database').connect();
import {deepChangeKeyCase} from '../util';
import * as feedCore from './feed-core';

function createOrUpdateMood(opts) {
  const upsertMoodSql = `
    WITH upsert AS
      (UPDATE
        wappu_mood
      SET
        rating = ?,
        description = ?,
        updated_at = CURRENT_TIMESTAMP
      WHERE
        user_id = ? AND created_at_coarse = CURRENT_DATE
      RETURNING
        *,
        false AS is_new
    ), inserted AS (
        INSERT INTO
          wappu_mood(user_id, rating, description)
        SELECT ?, ?, ? WHERE NOT EXISTS( SELECT * FROM upsert )
        RETURNING
          *,
          true AS is_new
    )
    SELECT *
    FROM upsert
    UNION ALL
    SELECT *
    FROM inserted;
  `;

  let params = [
    opts.rating, opts.description, opts.client.id,
    opts.client.id, opts.rating, opts.description
  ];

  return knex.transaction(trx =>
    trx.raw(upsertMoodSql, params)
      .then(result => {
        const mood = result.rows[0];
        if (mood.is_new) feedCore.createFeedItem(_feedTemplate(mood, opts.client));
        return undefined;
      })
      .catch(err => {
        err.message = 'Error updating database';
        err.status = 500;
        throw err;
      })
  );
}

function getMood(client) {
  const getMoodSql = `
    SELECT
      date,
      rating_city,
      rating_team,
      rating_personal
    FROM (
      SELECT date::DATE
      FROM   generate_series(
        '${process.env.MOOD_START_DATE}'::DATE,
        '${process.env.MOOD_END_DATE}'::DATE,
        interval '1 day'
      ) date
    ) dates LEFT JOIN LATERAL (
       SELECT
         ROUND(AVG(wappu_mood.rating)::numeric, 4) AS rating_city
       FROM
         wappu_mood
         LEFT JOIN users ON users.id = wappu_mood.user_id
         LEFT JOIN teams ON teams.id = users.team_id
       WHERE
         teams.city_id = (SELECT city_id FROM teams WHERE id = ?) AND
         wappu_mood.created_at_coarse = date
    ) city_score ON true LEFT JOIN LATERAL (
       SELECT
         ROUND(AVG(wappu_mood.rating)::numeric, 4) AS rating_team
       FROM
         wappu_mood
         LEFT JOIN users ON users.id = wappu_mood.user_id
       WHERE
         users.team_id = ? AND date = wappu_mood.created_at_coarse
    ) team_score ON true LEFT JOIN LATERAL (
       SELECT
         ROUND(AVG(wappu_mood.rating)::numeric, 4) AS rating_personal
       FROM
         wappu_mood
       WHERE
         wappu_mood.user_id = ? AND date = wappu_mood.created_at_coarse
    ) personal_score ON true
    ORDER BY date ASC;
  `;

  return knex.transaction(trx =>
    trx.raw(getMoodSql, [client.team, client.team, client.id])
      .then(result => _rowsToMoodObjects(result.rows))
      .catch(err => {
        err.message = 'Error reading database';
        err.status = 500;
        throw err;
      })
  );
}

function _rowsToMoodObjects(rows) {
  return rows.map(row => deepChangeKeyCase(row, 'camelCase'));
}

function _feedTemplate(row, client) {
  return {
    user:  client.uuid,
    type: 'TEXT',
    text: `${ client.name }'s wappu fiba is ${ row.rating } - ${ row.description }`,
  }
}

export {
  createOrUpdateMood,
  getMood,
};
<commit_msg>Env variable check<commit_after>const {knex} = require('../util/database').connect();
import {deepChangeKeyCase} from '../util';
import * as feedCore from './feed-core';
const requireEnvs = require('../util/require-envs');

requireEnvs(['MOOD_START_DATE', 'MOOD_END_DATE']);

function createOrUpdateMood(opts) {
  const upsertMoodSql = `
    WITH upsert AS
      (UPDATE
        wappu_mood
      SET
        rating = ?,
        description = ?,
        updated_at = CURRENT_TIMESTAMP
      WHERE
        user_id = ? AND created_at_coarse = CURRENT_DATE
      RETURNING
        *,
        false AS is_new
    ), inserted AS (
        INSERT INTO
          wappu_mood(user_id, rating, description)
        SELECT ?, ?, ? WHERE NOT EXISTS( SELECT * FROM upsert )
        RETURNING
          *,
          true AS is_new
    )
    SELECT *
    FROM upsert
    UNION ALL
    SELECT *
    FROM inserted;
  `;

  let params = [
    opts.rating, opts.description, opts.client.id,
    opts.client.id, opts.rating, opts.description
  ];

  return knex.transaction(trx =>
    trx.raw(upsertMoodSql, params)
      .then(result => {
        const mood = result.rows[0];
        if (mood.is_new) feedCore.createFeedItem(_feedTemplate(mood, opts.client));
        return undefined;
      })
      .catch(err => {
        err.message = 'Error updating database';
        err.status = 500;
        throw err;
      })
  );
}

function getMood(client) {
  const getMoodSql = `
    SELECT
      date,
      rating_city,
      rating_team,
      rating_personal
    FROM (
      SELECT date::DATE
      FROM   generate_series(
        '${process.env.MOOD_START_DATE}'::DATE,
        '${process.env.MOOD_END_DATE}'::DATE,
        interval '1 day'
      ) date
    ) dates LEFT JOIN LATERAL (
       SELECT
         ROUND(AVG(wappu_mood.rating)::numeric, 4) AS rating_city
       FROM
         wappu_mood
         LEFT JOIN users ON users.id = wappu_mood.user_id
         LEFT JOIN teams ON teams.id = users.team_id
       WHERE
         teams.city_id = (SELECT city_id FROM teams WHERE id = ?) AND
         wappu_mood.created_at_coarse = date
    ) city_score ON true LEFT JOIN LATERAL (
       SELECT
         ROUND(AVG(wappu_mood.rating)::numeric, 4) AS rating_team
       FROM
         wappu_mood
         LEFT JOIN users ON users.id = wappu_mood.user_id
       WHERE
         users.team_id = ? AND date = wappu_mood.created_at_coarse
    ) team_score ON true LEFT JOIN LATERAL (
       SELECT
         ROUND(AVG(wappu_mood.rating)::numeric, 4) AS rating_personal
       FROM
         wappu_mood
       WHERE
         wappu_mood.user_id = ? AND date = wappu_mood.created_at_coarse
    ) personal_score ON true
    ORDER BY date ASC;
  `;

  return knex.transaction(trx =>
    trx.raw(getMoodSql, [client.team, client.team, client.id])
      .then(result => _rowsToMoodObjects(result.rows))
      .catch(err => {
        err.message = 'Error reading database';
        err.status = 500;
        throw err;
      })
  );
}

function _rowsToMoodObjects(rows) {
  return rows.map(row => deepChangeKeyCase(row, 'camelCase'));
}

function _feedTemplate(row, client) {
  return {
    user:  client.uuid,
    type: 'TEXT',
    text: `${ client.name }'s wappu fiba is ${ row.rating } - ${ row.description }`,
  }
}

export {
  createOrUpdateMood,
  getMood,
};
<|endoftext|>


<commit_before>import User from '../models/Users';

const userController = {

  list: (request, response, next) => {
    User.find({}).exec()
    .then(Users => {
      return response.json(Users);
    })
    .catch(err => {
      return next(err);
    });
  },

  show: (request, response, next) => {
    User.findById(request.params.id).exec()
    .then(Users => {
      return response.json(Users);
    })
    .catch(err => {
      return next(err);
    });
  },

  remove: (request, response, next) => {
    User.findByIdAndRemove(request.params.id).exec()
    .then(Users => {
      return response.json(Users);
    })
    .catch(err => {
      return next(err);
    });
  },

  create: (request, response, next) => {
    const user = new User(request.body);

    user.save()
    .then(storedUser => {
      return response.json(storedUser);
    })
    .catch(err => {
      return next(err);
    });
  },

  update: (request, response, next) => {
    User.findById(request.params.id).exec()
      .then(user => {
        user.firstName = request.body.firstName || user.firstName;
        user.lastName = request.body.lastName || user.lastName;
        user.avatar = request.body.avatar || user.avatar;
        user.interests = request.body.interests || user.interests;
        user.userName = request.body.userName || user.userName;

        return user.save();
      })
      .then(changedUser => {
        return response.json(changedUser);
      })
      .catch(err => {
        return next(err);
      });
  }

};

export default userController;
<commit_msg>changing path names<commit_after>import User from './models/Users';

const userController = {

  list: (request, response, next) => {
    User.find({}).exec()
    .then(Users => {
      return response.json(Users);
    })
    .catch(err => {
      return next(err);
    });
  },

  show: (request, response, next) => {
    User.findById(request.params.id).exec()
    .then(Users => {
      return response.json(Users);
    })
    .catch(err => {
      return next(err);
    });
  },

  remove: (request, response, next) => {
    User.findByIdAndRemove(request.params.id).exec()
    .then(Users => {
      return response.json(Users);
    })
    .catch(err => {
      return next(err);
    });
  },

  create: (request, response, next) => {
    const user = new User(request.body);

    user.save()
    .then(storedUser => {
      return response.json(storedUser);
    })
    .catch(err => {
      return next(err);
    });
  },

  update: (request, response, next) => {
    User.findById(request.params.id).exec()
      .then(user => {
        user.firstName = request.body.firstName || user.firstName;
        user.lastName = request.body.lastName || user.lastName;
        user.avatar = request.body.avatar || user.avatar;
        user.interests = request.body.interests || user.interests;
        user.userName = request.body.userName || user.userName;

        return user.save();
      })
      .then(changedUser => {
        return response.json(changedUser);
      })
      .catch(err => {
        return next(err);
      });
  }

};

export default userController;
<|endoftext|>


<commit_before>import { initialGeneralSettings } from 'core/reducers/defaults';
import { addTabListeners } from '../side-effects';
import {
  favIconFallback,
  alertCircle,
  tabList,
  searchInput,
  d,
  WORDBREAK_ALL_CLASSNAME,
  SELECTED_TAB_CLASSNAME,
  BOOKMARK_TYPE,
  HISTORY_TYPE,
  TAB_PIN_CLASSNAME,
  TAB_MUTED_CLASSNAME,
  TAB_ACTIVE_ID,
} from '../constants';
import { badFavIconCache } from '../caches';
import {
  filledHistorySvg,
  filledBookmarkSvg,
} from '../assets';

const changeHeadTabListNodeSelectedStyle = method => () => {
  tabList.firstElementChild.classList[method](SELECTED_TAB_CLASSNAME);
};

export const removeHeadTabListNodeSelectedStyle =
  changeHeadTabListNodeSelectedStyle('remove');

export const addHeadTabListNodeSelectedStyle =
  changeHeadTabListNodeSelectedStyle('add');

export function isTabListEmpty() {
  return !tabList.firstElementChild.classList.contains('tab-object');
}

// Save the default values for font sizes so we know when to inline the font
// sizes when we append tab nodes
const {
  tabUrlSize: defaultTabUrlSize,
  tabTitleSize: defaultTabTitleSize,
} = initialGeneralSettings;

// Store all the bad favIcons so we don't get loading jank if a favIcon !exist
function clearChildren(node) {
  while (node.firstChild) {
    node.removeChild(node.firstChild);
  }
}

// Given a string, return true if one string is too long and should apply
function shouldWordBreak(str) {
  const MAX_LENGTH = 40;
  // eslint-disable-next-line arrow-body-style
  const longestWordLength = (acc, s) => s.length > acc
    ? s.length
    : acc;
  const longest = str.split(/\s/).reduce(longestWordLength, 0);
  return longest > MAX_LENGTH;
}

export function tabToTag(getState) {
  const {
    tabUrlSize,
    tabTitleSize,
  } = getState().general;
  return function doCreateTabObject(tab) {
    const {
      favIconUrl,
      title,
      id,
      url,
      type,
      sessionId,
      windowId,
      mutedInfo,
      pinned,
      isActive,
    } = tab;
    const isValidFavIconUrl = favIconUrl
      && !badFavIconCache().includes(favIconUrl);
    // Since favicon url of bookmarks isn't readily available,
    // check the type and assign all bookmarks to the static svg
    // for now.
    let favIconLink;
    switch (type) {
      case HISTORY_TYPE:
        favIconLink = filledHistorySvg;
        break;
      case BOOKMARK_TYPE:
        favIconLink = filledBookmarkSvg;
        break;
      default:
        favIconLink = isValidFavIconUrl ? favIconUrl : favIconFallback;
        break;
    }
    return createTabObject({
      id,
      url,
      title,
      favIconLink,
      type,
      sessionId,
      windowId,
      tabUrlSize,
      tabTitleSize,
      mutedInfo,
      pinned,
      isActive,
    }, shouldWordBreak(title));
  };
}

function createTabObject({
  id,
  sessionId,
  type,
  url,
  title,
  favIconLink,
  windowId,
  lastAccessed,
  tabUrlSize,
  tabTitleSize,
  mutedInfo,
  pinned,
  isActive,
}, wordBreak) {
  const dataId = sessionId || id;
  // Create the parent div
  //

  const tabObjectNode = d.createElement('div');
  tabObjectNode.setAttribute('tabindex', '0');
  tabObjectNode.classList.add('tab-object');
  tabObjectNode.classList.add(type);
  // Declare id used for switching
  tabObjectNode.setAttribute('data-id', dataId);
  // Declare data type to know what to paint
  tabObjectNode.setAttribute('data-type', type);
  // Add window id
  tabObjectNode.setAttribute('data-window', windowId);
  // Add url
  tabObjectNode.setAttribute('data-url', url);
  // Add lastaccessed
  tabObjectNode.setAttribute('data-last-accessed', lastAccessed);

  //
  const iconContainerNode = d.createElement('div');
  iconContainerNode.classList.add('tab-img');

  const favIconNode = d.createElement('img');
  favIconNode.setAttribute('src', favIconLink);
  favIconNode.onerror = handleBadSvg;

  iconContainerNode.appendChild(favIconNode);

  // tab-info
  //

  const tabInfoNode = d.createElement('div');
  tabInfoNode.setAttribute('class', 'tab-info');

  //      ${title}
  // INLINE tabTitleSize HERE
  const titleNode = d.createElement('div');
  titleNode.classList.add('tab-title');
  if (wordBreak) {
    titleNode.classList.add(WORDBREAK_ALL_CLASSNAME);
  }
  titleNode.appendChild(d.createTextNode(title));
  if (tabTitleSize !== defaultTabTitleSize) {
    titleNode.style = `font-size: ${tabTitleSize}px`;
  }

  //
${url}

  // INLINE tabUrlSize HERE
  const urlNode = d.createElement('p');
  urlNode.classList.add('tab-url');
  urlNode.appendChild(d.createTextNode(url));
  if (tabUrlSize !== defaultTabUrlSize) {
    urlNode.style = `font-size: ${tabUrlSize}px`;
  }

  // Append all block elements
  tabInfoNode.appendChild(titleNode);
  // Don't show the url if user specifies 0 as the url font size
  if (tabUrlSize > 0) {
    tabInfoNode.appendChild(urlNode);
  }

  tabObjectNode.appendChild(iconContainerNode);
  tabObjectNode.appendChild(tabInfoNode);

  if (pinned) {
    tabObjectNode.classList.add(TAB_PIN_CLASSNAME);
  }
  if (mutedInfo && mutedInfo.muted) {
    tabObjectNode.classList.add(TAB_MUTED_CLASSNAME);
  }
  if (isActive) {
    tabObjectNode.id = TAB_ACTIVE_ID;
  }

  return tabObjectNode;
}

function handleBadSvg() {
  badFavIconCache().push(this.src);
  this.src = favIconFallback;
}

// Get focused node's position relative to the current scrolled view
export function getNodePositions(parentNode, selectedNode) {
  const parentHeight = parentNode.offsetHeight;
  const selectedTop = (
    selectedNode.offsetTop - parentNode.offsetTop
  ) - parentNode.scrollTop;
  const selectedBottom = selectedNode.offsetHeight + selectedTop;
  return {
    parentHeight,
    selectedBottom,
    selectedTop,
  };
}

export function createNoResult() {
  //

  //   <object type="image/svg+xml" data="/assets/alert-circle.svg"></object>
  //   No tabs found
  //

  const noResultNode = d.createElement('div');
  noResultNode.classList.add('no-result');

  const alertCircleNode = d.createElement('object');
  alertCircleNode.classList.add('no-result__circle');
  alertCircleNode.setAttribute('type', 'image/svg+xml');
  alertCircleNode.setAttribute('data', alertCircle);

  const strongMsgNode = d.createElement('strong');
  strongMsgNode.classList.add('no-result__msg');
  strongMsgNode.appendChild(d.createTextNode('No tabs found'));

  noResultNode.appendChild(alertCircleNode);
  noResultNode.appendChild(strongMsgNode);
  return noResultNode;
}

export function injectTabsInList(getState) {
  return function doInjectTabsInList(tabArray) {
    const wasNoResult = tabList.querySelectorAll('.tab-object').length === 0;
    const showNoResult = tabArray.length === 0;
    // Don't update dom if we're going to show no results again
    if (wasNoResult && showNoResult) return tabArray;

    clearChildren(tabList);

    // Add nodes to tabList & attach event listeners
    if (showNoResult) {
      tabList.appendChild(createNoResult());
    } else {
      tabArray.map(tabToTag(getState)).forEach((tabNode) => {
        addTabListeners(getState)(tabNode);
        tabList.appendChild(tabNode);
      });
    }
    return tabArray;
  };
}

export function scrollIfNeeded(event) {
  // If the selected tab isn't completely visible in the scrolled view,
  // force scroll
  const {
    parentHeight,
    selectedBottom,
    selectedTop,
  } = getNodePositions(event.target.parentNode, event.target);
  const shouldScrollDown = selectedBottom > parentHeight;
  const shouldScrollUp = selectedTop < 0;

  if (!shouldScrollDown && !shouldScrollUp) return;

  if (shouldScrollDown) {
    event.target.scrollIntoView(false);
  } else if (shouldScrollUp) {
    event.target.scrollIntoView(true);
  }
}

export function removeElementFromTabList(element, wasClicked) {
  const nextElementToFocus = element.nextSibling
    ? element.nextSibling
    : element.previousSibling;
  tabList.removeChild(element);
  if (!wasClicked) {
    nextElementToFocus.focus();
  }
}

export function repaintElementWithType(element, typeToAdd) {
  const { type: typeToRemove } = element.dataset;
  element.classList.remove(typeToRemove);
  element.classList.add(typeToAdd);
  element.dataset.type = typeToAdd;
}

// Function for initializing the lists
export function populateTabList(search) {
  return Promise.resolve(search).then(injectTabsInList);
}

export function overrideFontStylesWithSansSerif() {
  const elementStylesToOverride = [
    d.body,
    d.querySelector('.search'),
  ];
  elementStylesToOverride.forEach((element) => {
    element.style = 'font-family: sans-serif;';
  });
}

export function appendSearchInputPlaceholderText(newText) {
  // eslint-disable-next-line prefer-template
  searchInput.placeholder += ' ' + newText;
}

export function selectNodeText(nodeToCopy) {
  const range = d.createRange();
  range.selectNode(nodeToCopy);
  window.getSelection().addRange(range);
}
<commit_msg>Give an id for no-result<commit_after>import { initialGeneralSettings } from 'core/reducers/defaults';
import { addTabListeners } from '../side-effects';
import {
  favIconFallback,
  alertCircle,
  tabList,
  searchInput,
  d,
  WORDBREAK_ALL_CLASSNAME,
  SELECTED_TAB_CLASSNAME,
  BOOKMARK_TYPE,
  HISTORY_TYPE,
  TAB_PIN_CLASSNAME,
  TAB_MUTED_CLASSNAME,
  TAB_ACTIVE_ID,
} from '../constants';
import { badFavIconCache } from '../caches';
import {
  filledHistorySvg,
  filledBookmarkSvg,
} from '../assets';

const changeHeadTabListNodeSelectedStyle = method => () => {
  tabList.firstElementChild.classList[method](SELECTED_TAB_CLASSNAME);
};

export const removeHeadTabListNodeSelectedStyle =
  changeHeadTabListNodeSelectedStyle('remove');

export const addHeadTabListNodeSelectedStyle =
  changeHeadTabListNodeSelectedStyle('add');

export function isTabListEmpty() {
  return !tabList.firstElementChild.classList.contains('tab-object');
}

// Save the default values for font sizes so we know when to inline the font
// sizes when we append tab nodes
const {
  tabUrlSize: defaultTabUrlSize,
  tabTitleSize: defaultTabTitleSize,
} = initialGeneralSettings;

// Store all the bad favIcons so we don't get loading jank if a favIcon !exist
function clearChildren(node) {
  while (node.firstChild) {
    node.removeChild(node.firstChild);
  }
}

// Given a string, return true if one string is too long and should apply
function shouldWordBreak(str) {
  const MAX_LENGTH = 40;
  // eslint-disable-next-line arrow-body-style
  const longestWordLength = (acc, s) => s.length > acc
    ? s.length
    : acc;
  const longest = str.split(/\s/).reduce(longestWordLength, 0);
  return longest > MAX_LENGTH;
}

export function tabToTag(getState) {
  const {
    tabUrlSize,
    tabTitleSize,
  } = getState().general;
  return function doCreateTabObject(tab) {
    const {
      favIconUrl,
      title,
      id,
      url,
      type,
      sessionId,
      windowId,
      mutedInfo,
      pinned,
      isActive,
    } = tab;
    const isValidFavIconUrl = favIconUrl
      && !badFavIconCache().includes(favIconUrl);
    // Since favicon url of bookmarks isn't readily available,
    // check the type and assign all bookmarks to the static svg
    // for now.
    let favIconLink;
    switch (type) {
      case HISTORY_TYPE:
        favIconLink = filledHistorySvg;
        break;
      case BOOKMARK_TYPE:
        favIconLink = filledBookmarkSvg;
        break;
      default:
        favIconLink = isValidFavIconUrl ? favIconUrl : favIconFallback;
        break;
    }
    return createTabObject({
      id,
      url,
      title,
      favIconLink,
      type,
      sessionId,
      windowId,
      tabUrlSize,
      tabTitleSize,
      mutedInfo,
      pinned,
      isActive,
    }, shouldWordBreak(title));
  };
}

function createTabObject({
  id,
  sessionId,
  type,
  url,
  title,
  favIconLink,
  windowId,
  lastAccessed,
  tabUrlSize,
  tabTitleSize,
  mutedInfo,
  pinned,
  isActive,
}, wordBreak) {
  const dataId = sessionId || id;
  // Create the parent div
  //

  const tabObjectNode = d.createElement('div');
  tabObjectNode.setAttribute('tabindex', '0');
  tabObjectNode.classList.add('tab-object');
  tabObjectNode.classList.add(type);
  // Declare id used for switching
  tabObjectNode.setAttribute('data-id', dataId);
  // Declare data type to know what to paint
  tabObjectNode.setAttribute('data-type', type);
  // Add window id
  tabObjectNode.setAttribute('data-window', windowId);
  // Add url
  tabObjectNode.setAttribute('data-url', url);
  // Add lastaccessed
  tabObjectNode.setAttribute('data-last-accessed', lastAccessed);

  //
  const iconContainerNode = d.createElement('div');
  iconContainerNode.classList.add('tab-img');

  const favIconNode = d.createElement('img');
  favIconNode.setAttribute('src', favIconLink);
  favIconNode.onerror = handleBadSvg;

  iconContainerNode.appendChild(favIconNode);

  // tab-info
  //

  const tabInfoNode = d.createElement('div');
  tabInfoNode.setAttribute('class', 'tab-info');

  //      ${title}
  // INLINE tabTitleSize HERE
  const titleNode = d.createElement('div');
  titleNode.classList.add('tab-title');
  if (wordBreak) {
    titleNode.classList.add(WORDBREAK_ALL_CLASSNAME);
  }
  titleNode.appendChild(d.createTextNode(title));
  if (tabTitleSize !== defaultTabTitleSize) {
    titleNode.style = `font-size: ${tabTitleSize}px`;
  }

  //
${url}

  // INLINE tabUrlSize HERE
  const urlNode = d.createElement('p');
  urlNode.classList.add('tab-url');
  urlNode.appendChild(d.createTextNode(url));
  if (tabUrlSize !== defaultTabUrlSize) {
    urlNode.style = `font-size: ${tabUrlSize}px`;
  }

  // Append all block elements
  tabInfoNode.appendChild(titleNode);
  // Don't show the url if user specifies 0 as the url font size
  if (tabUrlSize > 0) {
    tabInfoNode.appendChild(urlNode);
  }

  tabObjectNode.appendChild(iconContainerNode);
  tabObjectNode.appendChild(tabInfoNode);

  if (pinned) {
    tabObjectNode.classList.add(TAB_PIN_CLASSNAME);
  }
  if (mutedInfo && mutedInfo.muted) {
    tabObjectNode.classList.add(TAB_MUTED_CLASSNAME);
  }
  if (isActive) {
    tabObjectNode.id = TAB_ACTIVE_ID;
  }

  return tabObjectNode;
}

function handleBadSvg() {
  badFavIconCache().push(this.src);
  this.src = favIconFallback;
}

// Get focused node's position relative to the current scrolled view
export function getNodePositions(parentNode, selectedNode) {
  const parentHeight = parentNode.offsetHeight;
  const selectedTop = (
    selectedNode.offsetTop - parentNode.offsetTop
  ) - parentNode.scrollTop;
  const selectedBottom = selectedNode.offsetHeight + selectedTop;
  return {
    parentHeight,
    selectedBottom,
    selectedTop,
  };
}

export function createNoResult() {
  //

  //   <object type="image/svg+xml" data="/assets/alert-circle.svg"></object>
  //   No tabs found
  //

  const noResultNode = d.createElement('div');
  noResultNode.classList.add('no-result');

  const alertCircleNode = d.createElement('object');
  alertCircleNode.classList.add('no-result__circle');
  alertCircleNode.setAttribute('type', 'image/svg+xml');
  alertCircleNode.setAttribute('data', alertCircle);

  const strongMsgNode = d.createElement('strong');
  strongMsgNode.classList.add('no-result__msg');
  strongMsgNode.appendChild(d.createTextNode('No tabs found'));

  noResultNode.appendChild(alertCircleNode);
  noResultNode.appendChild(strongMsgNode);
  noResultNode.id = 'no-result';
  return noResultNode;
}

export function injectTabsInList(getState) {
  return function doInjectTabsInList(tabArray) {
    const wasNoResult = tabList.querySelectorAll('.tab-object').length === 0;
    const showNoResult = tabArray.length === 0;
    // Don't update dom if we're going to show no results again
    if (wasNoResult && showNoResult) return tabArray;

    clearChildren(tabList);

    // Add nodes to tabList & attach event listeners
    if (showNoResult) {
      tabList.appendChild(createNoResult());
    } else {
      tabArray.map(tabToTag(getState)).forEach((tabNode) => {
        addTabListeners(getState)(tabNode);
        tabList.appendChild(tabNode);
      });
    }
    return tabArray;
  };
}

export function scrollIfNeeded(event) {
  // If the selected tab isn't completely visible in the scrolled view,
  // force scroll
  const {
    parentHeight,
    selectedBottom,
    selectedTop,
  } = getNodePositions(event.target.parentNode, event.target);
  const shouldScrollDown = selectedBottom > parentHeight;
  const shouldScrollUp = selectedTop < 0;

  if (!shouldScrollDown && !shouldScrollUp) return;

  if (shouldScrollDown) {
    event.target.scrollIntoView(false);
  } else if (shouldScrollUp) {
    event.target.scrollIntoView(true);
  }
}

export function removeElementFromTabList(element, wasClicked) {
  const nextElementToFocus = element.nextSibling
    ? element.nextSibling
    : element.previousSibling;
  tabList.removeChild(element);
  if (!wasClicked) {
    nextElementToFocus.focus();
  }
}

export function repaintElementWithType(element, typeToAdd) {
  const { type: typeToRemove } = element.dataset;
  element.classList.remove(typeToRemove);
  element.classList.add(typeToAdd);
  element.dataset.type = typeToAdd;
}

// Function for initializing the lists
export function populateTabList(search) {
  return Promise.resolve(search).then(injectTabsInList);
}

export function overrideFontStylesWithSansSerif() {
  const elementStylesToOverride = [
    d.body,
    d.querySelector('.search'),
  ];
  elementStylesToOverride.forEach((element) => {
    element.style = 'font-family: sans-serif;';
  });
}

export function appendSearchInputPlaceholderText(newText) {
  // eslint-disable-next-line prefer-template
  searchInput.placeholder += ' ' + newText;
}

export function selectNodeText(nodeToCopy) {
  const range = d.createRange();
  range.selectNode(nodeToCopy);
  window.getSelection().addRange(range);
}
<|endoftext|>


<commit_before>var _ = require('underscore');
var Backbone = require('backbone');
var CoreView = require('backbone/core-view');
var StackLayoutView = require('../../../../../../components/stack-layout/stack-layout-view');
var ColumnListView = require('../../column-list-view');
var quantificationMethodItemTemplate = require('../quantification-method-item.tpl');
var InputRampContentView = require('./input-ramp-content-view');
var rampList = require('cartocolor');

var QUANTIFICATION_METHODS = ['quantiles', 'jenks', 'equal', 'headtails', 'category'];
var BINS = ['2', '3', '4', '5', '6', '7'];

module.exports = CoreView.extend({
  initialize: function (opts) {
    this._setupModel();
    this._initBinds();
    this._initViews();
  },

  render: function () {
    this.clearSubViews();
    this.$el.empty();

    this.$el.append(this._stackLayoutView.render().$el);

    return this;
  },

  _setupModel: function () {
    var options = {};

    if (!this.model.get('quantification')) {
      options.quantification = QUANTIFICATION_METHODS[0];
    }

    if (!this.model.get('bins')) {
      options.bins = BINS[1]; // select '3' as the default bin size
    }

    if (this.model.get('bins') > BINS[BINS.length - 1]) {
      options.bins = BINS[BINS.length - 1];
    }

    this.model.set(options);
  },

  _initBinds: function () {
    var range = this.model.get('range');

    this.model.bind('change:bins', this._updateRange, this);
    this.model.bind('change:attribute', this.render, this);

    this.model.on('change:quantification', this._onChangeQuantification, this);

    this.model.unset('fixed');

    if (!range || !this._isValidRange(range)) {
      this.model.set('range', this._getDefaultRamp());
    }
  },

  // range minimun size should be 2
  _isValidRange: function (range) {
    return range.length >= 2;
  },

  _initViews: function () {
    var self = this;

    var stackViewCollection = new Backbone.Collection([{
      createStackView: function (stackLayoutModel, opts) {
        return self._createInputRampContentView(stackLayoutModel, opts).bind(self);
      }
    }, {
      createStackView: function (stackLayoutModel, opts) {
        return self._createQuantificationListView(stackLayoutModel, opts).bind(self);
      }
    }, {
      createStackView: function (stackLayoutModel, opts) {
        return self._createBinsListView(stackLayoutModel, opts).bind(self);
      }
    }]);

    this._stackLayoutView = new StackLayoutView({
      collection: stackViewCollection
    });
  },

  _updateRange: function () {
    var previousBins = this.model.previous('bins');

    if (!previousBins) {
      return;
    }

    var previousRamps = _.find(rampList, function (ramp) {
      var previousRamp = ramp[previousBins];
      var range = this.model.get('range');
      return previousRamp && range && previousRamp.join('').toLowerCase() === range.join('').toLowerCase();
    }, this);

    if (previousRamps) {
      var bins = this.model.get('bins');
      this.model.set('range', previousRamps[bins]);
    }
  },

  _createInputRampContentView: function (stackLayoutModel, opts) {
    var view = new InputRampContentView({
      stackLayoutModel: stackLayoutModel,
      model: this.model
    });

    view.bind('back', function (value) {
      this.trigger('back');
    }, this);

    view.bind('selectItem', function (value) {
      this.model.unset('domain');
      this.model.set('range', value);
    }, this);

    view.bind('selectQuantification', function (value) {
      stackLayoutModel.goToStep(1);
    }, this);

    view.bind('selectBins', function (value) {
      stackLayoutModel.goToStep(2);
    }, this);

    return view;
  },

  _createBinsListView: function (stackLayoutModel, opts) {
    var view = new ColumnListView({
      headerTitle: _t('form-components.editors.fill.bins'),
      stackLayoutModel: stackLayoutModel,
      columns: BINS
    });

    view.bind('selectItem', function (item) {
      this.model.set('bins', item.get('val'));
      stackLayoutModel.goToStep(0);
    }, this);

    view.bind('back', function (value) {
      stackLayoutModel.goToStep(0);
    }, this);

    return view;
  },

  _createQuantificationListView: function (stackLayoutModel, opts) {
    var view = new ColumnListView({
      headerTitle: _t('form-components.editors.fill.quantification.title'),
      stackLayoutModel: stackLayoutModel,
      itemTemplate: quantificationMethodItemTemplate,
      columns: QUANTIFICATION_METHODS,
      showSearch: false
    });

    view.bind('selectItem', function (item) {
      this.model.set('quantification', item.get('val'));
      stackLayoutModel.prevStep();
    }, this);

    view.bind('back', function (value) {
      stackLayoutModel.goToStep(0);
    }, this);

    return view;
  },

  _getDefaultRamp: function () {
    return _.map(rampList, function (ramp) {
      return ramp[this.model.get('bins')];
    }, this)[0];
  },

  _onChangeQuantification: function () {
    if (this.model.get('quantification') === 'category') {
      this.trigger('switch', this);
    }
  },

  remove: function () {
    CoreView.prototype.remove.apply(this);
  }
});
<commit_msg>CR taken.<commit_after>var _ = require('underscore');
var Backbone = require('backbone');
var CoreView = require('backbone/core-view');
var StackLayoutView = require('../../../../../../components/stack-layout/stack-layout-view');
var ColumnListView = require('../../column-list-view');
var quantificationMethodItemTemplate = require('../quantification-method-item.tpl');
var InputRampContentView = require('./input-ramp-content-view');
var rampList = require('cartocolor');

var QUANTIFICATION_METHODS = ['quantiles', 'jenks', 'equal', 'headtails', 'category'];
var BINS = ['2', '3', '4', '5', '6', '7'];

module.exports = CoreView.extend({
  initialize: function (opts) {
    this._setupModel();
    this._initBinds();
    this._initViews();
  },

  render: function () {
    this.clearSubViews();
    this.$el.empty();

    this.$el.append(this._stackLayoutView.render().$el);

    return this;
  },

  _setupModel: function () {
    var options = {};

    if (!this.model.get('quantification')) {
      options.quantification = QUANTIFICATION_METHODS[0];
    }

    if (!this.model.get('bins')) {
      options.bins = BINS[1]; // select '3' as the default bin size
    }

    if (this.model.get('bins') > BINS[BINS.length - 1]) {
      options.bins = BINS[BINS.length - 1];
    }

    this.model.set(options);
  },

  _initBinds: function () {
    var range = this.model.get('range');

    this.model.bind('change:bins', this._updateRange, this);
    this.model.bind('change:attribute', this.render, this);

    this.model.on('change:quantification', this._onChangeQuantification, this);

    this.model.unset('fixed');

    if (!range || !this._isValidRange(range)) {
      this.model.set('range', this._getDefaultRamp());
    }
  },

  // range minimun size should be 2
  _isValidRange: function (range) {
    return range && range.length > 1 || false;
  },

  _initViews: function () {
    var self = this;

    var stackViewCollection = new Backbone.Collection([{
      createStackView: function (stackLayoutModel, opts) {
        return self._createInputRampContentView(stackLayoutModel, opts).bind(self);
      }
    }, {
      createStackView: function (stackLayoutModel, opts) {
        return self._createQuantificationListView(stackLayoutModel, opts).bind(self);
      }
    }, {
      createStackView: function (stackLayoutModel, opts) {
        return self._createBinsListView(stackLayoutModel, opts).bind(self);
      }
    }]);

    this._stackLayoutView = new StackLayoutView({
      collection: stackViewCollection
    });
  },

  _updateRange: function () {
    var previousBins = this.model.previous('bins');

    if (!previousBins) {
      return;
    }

    var previousRamps = _.find(rampList, function (ramp) {
      var previousRamp = ramp[previousBins];
      var range = this.model.get('range');
      return previousRamp && range && previousRamp.join('').toLowerCase() === range.join('').toLowerCase();
    }, this);

    if (previousRamps) {
      var bins = this.model.get('bins');
      this.model.set('range', previousRamps[bins]);
    }
  },

  _createInputRampContentView: function (stackLayoutModel, opts) {
    var view = new InputRampContentView({
      stackLayoutModel: stackLayoutModel,
      model: this.model
    });

    view.bind('back', function (value) {
      this.trigger('back');
    }, this);

    view.bind('selectItem', function (value) {
      this.model.unset('domain');
      this.model.set('range', value);
    }, this);

    view.bind('selectQuantification', function (value) {
      stackLayoutModel.goToStep(1);
    }, this);

    view.bind('selectBins', function (value) {
      stackLayoutModel.goToStep(2);
    }, this);

    return view;
  },

  _createBinsListView: function (stackLayoutModel, opts) {
    var view = new ColumnListView({
      headerTitle: _t('form-components.editors.fill.bins'),
      stackLayoutModel: stackLayoutModel,
      columns: BINS
    });

    view.bind('selectItem', function (item) {
      this.model.set('bins', item.get('val'));
      stackLayoutModel.goToStep(0);
    }, this);

    view.bind('back', function (value) {
      stackLayoutModel.goToStep(0);
    }, this);

    return view;
  },

  _createQuantificationListView: function (stackLayoutModel, opts) {
    var view = new ColumnListView({
      headerTitle: _t('form-components.editors.fill.quantification.title'),
      stackLayoutModel: stackLayoutModel,
      itemTemplate: quantificationMethodItemTemplate,
      columns: QUANTIFICATION_METHODS,
      showSearch: false
    });

    view.bind('selectItem', function (item) {
      this.model.set('quantification', item.get('val'));
      stackLayoutModel.prevStep();
    }, this);

    view.bind('back', function (value) {
      stackLayoutModel.goToStep(0);
    }, this);

    return view;
  },

  _getDefaultRamp: function () {
    return _.map(rampList, function (ramp) {
      return ramp[this.model.get('bins')];
    }, this)[0];
  },

  _onChangeQuantification: function () {
    if (this.model.get('quantification') === 'category') {
      this.trigger('switch', this);
    }
  },

  remove: function () {
    CoreView.prototype.remove.apply(this);
  }
});
<|endoftext|>


<commit_before>'use strict';

var httpMethods = require('methods');
var Readable = require('readable-stream/readable');
var isReadable = require('is-readable-stream');
var inherits = require('util').inherits;

function populateObjArr(srcObj, destObj, destArr) {
  Object.keys(srcObj).forEach(function (key) {
    var val = srcObj[key];
    if (val !== undefined) {
      val += '';
      destObj[key.toLowerCase()] = val;
      destArr.push(key);
      destArr.push(val);
    }
  });
}

function canHaveBody(mock) {
  return ['GET', 'HEAD', 'DELETE'].indexOf(mock.method) === -1;
}

var HTTP_VERSION_REGEXP = /^(\d+)\.(\d+)$/;

/**
 * @constructor
 * @param {string} [method=GET] HTTP method
 * @param {url} [url=/] URL
 * @param {object} [props={}] Additional properties, like `headers`
 */
function MockRequest(method, url, props) {
  if (!(this instanceof MockRequest)) {
    return new MockRequest(method, url, props);
  }

  var mock = this;

  Readable.call(mock);

  mock.connection = mock.socket = mock.client = mock.statusCode = mock.statusMessage = null;

  props = props || {};

  Object.keys(props).forEach(function (key) {
    mock[key] = props[key];
  });

  mock.url = url || '/';

  if (typeof method === 'string' && method) {
    mock.method = method.toUpperCase();
  }
  if (!mock.method || httpMethods.indexOf(mock.method.toLowerCase()) === -1) {
    mock.method = 'GET';
  }

  var ver = props.httpVersion;
  var verMatch = typeof ver === 'string' ? ver.match(HTTP_VERSION_REGEXP) : null;
  if (verMatch === null) {
    mock.httpVersion = '1.1';
    mock.httpVersionMajor = mock.httpVersionMinor = 1;
  } else {
    mock.httpVersionMajor = parseInt(verMatch[1], 10);
    mock.httpVersionMinor = parseInt(verMatch[2], 10);
  }

  mock.headers = {};
  mock.rawHeaders = [];
  populateObjArr(props.headers || {}, mock.headers, mock.rawHeaders);

  mock.trailers = {};
  mock.rawTrailers = [];
  function populateTrailersOnEnd() {
    if (props.trailers) {
      mock.trailers = {};
      mock.rawTrailers = [];
      populateObjArr(props.trailers, mock.trailers, mock.rawTrailers);
    }
  }

  if (!canHaveBody(mock)) {
    mock.push(null);
    populateTrailersOnEnd();
  } else {
    mock.once('end', populateTrailersOnEnd);
  }

  if (props.source !== undefined) {
    mock._setSource(props.source);
  }
}

inherits(MockRequest, Readable);

/**
 * See
 * [http.IncomingMessage#setTimeout](https://nodejs.org/api/http.html#http_message_settimeout_msecs_callback)
 */
MockRequest.prototype.setTimeout = function (msecs, callback) {
  setTimeout(callback, msecs);
};

/**
 * Set the source, the mock will pass through all data and events. When using a source stream, do
 * not override `_read()`!
 *
 * @param {object|string|Buffer} src Readable stream, string or Buffer
 */
MockRequest.prototype._setSource = function (src) {
  var mock = this;

  if (mock._sourceStreamSet) {
    throw new Error('Cannot set another source stream');
  }
  mock._sourceStreamSet = true;

  if (!canHaveBody(mock)) {
    throw new Error('Cannot pipe data into a GET/HEAD/DELETE request mock');
  }

  if (src === null || typeof src === 'string' || Buffer.isBuffer(src)) {
    if (src !== null) {
      mock.push(src);
    }
    return mock.push(null);
  } else if (!isReadable(src)) {
    throw new TypeError('src must be a string, Buffer or readable stream');
  }

  // Await first read attempt on the mock
  var waiting = false;
  var listeningOnReadable = false;

  mock._read = function () {
    var buf;
    var reads = 0;
    var interrupted = waiting = false;
    // Read as much as we can get from the source until the mock's buffer is full
    while ((buf = src.read()) !== null) {
      reads++;
      if (!mock.push(buf)) {
        interrupted = true;
        break;
      }
    }

    if (reads === 0 && !interrupted) {
      waiting = true;

      if (!listeningOnReadable) {
        listeningOnReadable = true;
        src.on('readable', onReadable);
      }
    }
  };

  function onReadable() {
    // Do nothing when nobody asked for the data
    if (waiting) {
      mock._read();
    }
  }

  function onEnd() {
    mock.push(null);
    removeListeners();
  }

  function onClose() {
    mock.push(null);
    mock.emit('close');
    removeListeners();
  }

  function onError(err) {
    mock.emit('error', err);
    removeListeners();
  }

  function removeListeners() {
    src.removeListener('readable', onReadable);
    src.removeListener('end', onEnd);
    src.removeListener('close', onClose);
    src.removeListener('error', onError);
  }

  src.once('end', onEnd);
  src.once('close', onClose);
  src.once('error', onError);
};

module.exports = MockRequest;
<commit_msg>Fix setting data source<commit_after>'use strict';

var httpMethods = require('methods');
var Readable = require('readable-stream/readable');
var isReadable = require('is-readable-stream');
var inherits = require('util').inherits;

function populateObjArr(srcObj, destObj, destArr) {
  Object.keys(srcObj).forEach(function (key) {
    var val = srcObj[key];
    if (val !== undefined) {
      val += '';
      destObj[key.toLowerCase()] = val;
      destArr.push(key);
      destArr.push(val);
    }
  });
}

function canHaveBody(mock) {
  return ['GET', 'HEAD', 'DELETE'].indexOf(mock.method) === -1;
}

var HTTP_VERSION_REGEXP = /^(\d+)\.(\d+)$/;

/**
 * @constructor
 * @param {string} [method=GET] HTTP method
 * @param {url} [url=/] URL
 * @param {object} [props={}] Additional properties, like `headers`
 */
function MockRequest(method, url, props) {
  if (!(this instanceof MockRequest)) {
    return new MockRequest(method, url, props);
  }

  var mock = this;

  Readable.call(mock);

  mock.connection = mock.socket = mock.client = mock.statusCode = mock.statusMessage = null;

  props = props || {};

  Object.keys(props).forEach(function (key) {
    mock[key] = props[key];
  });

  mock.url = url || '/';

  if (typeof method === 'string' && method) {
    mock.method = method.toUpperCase();
  }
  if (!mock.method || httpMethods.indexOf(mock.method.toLowerCase()) === -1) {
    mock.method = 'GET';
  }

  var ver = props.httpVersion;
  var verMatch = typeof ver === 'string' ? ver.match(HTTP_VERSION_REGEXP) : null;
  if (verMatch === null) {
    mock.httpVersion = '1.1';
    mock.httpVersionMajor = mock.httpVersionMinor = 1;
  } else {
    mock.httpVersionMajor = parseInt(verMatch[1], 10);
    mock.httpVersionMinor = parseInt(verMatch[2], 10);
  }

  mock.headers = {};
  mock.rawHeaders = [];
  populateObjArr(props.headers || {}, mock.headers, mock.rawHeaders);

  mock.trailers = {};
  mock.rawTrailers = [];
  function populateTrailersOnEnd() {
    if (props.trailers) {
      mock.trailers = {};
      mock.rawTrailers = [];
      populateObjArr(props.trailers, mock.trailers, mock.rawTrailers);
    }
  }

  if (!canHaveBody(mock)) {
    mock.push(null);
    populateTrailersOnEnd();
  } else {
    mock.once('end', populateTrailersOnEnd);
  }

  if (props.source !== undefined && canHaveBody(mock)) {
    mock._setSource(props.source);
  }
}

inherits(MockRequest, Readable);

/**
 * See
 * [http.IncomingMessage#setTimeout](https://nodejs.org/api/http.html#http_message_settimeout_msecs_callback)
 */
MockRequest.prototype.setTimeout = function (msecs, callback) {
  setTimeout(callback, msecs);
};

/**
 * Set the source, the mock will pass through all data and events. When using a source stream, do
 * not override `_read()`!
 *
 * @param {object|string|Buffer} src Readable stream, string or Buffer
 */
MockRequest.prototype._setSource = function (src) {
  var mock = this;

  if (!canHaveBody(mock)) {
    throw new Error('Cannot pipe data into a GET/HEAD/DELETE request mock');
  }

  if (mock._hasDataSource) {
    throw new Error('Cannot set another data source');
  }

  if (src === null || typeof src === 'string' || Buffer.isBuffer(src)) {
    if (src !== null) {
      mock.push(src);
    }

    mock._hasDataSource = true;

    return mock.push(null);
  }

  if (!isReadable(src)) {
    throw new TypeError('src must be a string, Buffer or readable stream');
  }

  mock._hasDataSource = true;

  // Await first read attempt on the mock
  var expectingMoreData = false;
  var listeningOnReadable = false;

  mock._read = function () {
    var buf;
    var reads = 0;
    var interrupted = expectingMoreData = false;
    // Read as much as we can get from the source until the mock's buffer is full
    while ((buf = src.read()) !== null) {
      reads++;
      if (!mock.push(buf)) {
        interrupted = true;
        break;
      }
    }

    if (reads === 0 && !interrupted) {
      expectingMoreData = true;

      if (!listeningOnReadable) {
        listeningOnReadable = true;
        src.on('readable', onReadable);
      }
    }
  };

  function onReadable() {
    // Do nothing when nobody asked for the data
    if (expectingMoreData) {
      mock._read();
    }
  }

  function onEnd() {
    mock.push(null);
    removeListeners();
  }

  function onClose() {
    mock.push(null);
    mock.emit('close');
    removeListeners();
  }

  function onError(err) {
    mock.emit('error', err);
    removeListeners();
  }

  function removeListeners() {
    src.removeListener('readable', onReadable);
    src.removeListener('end', onEnd);
    src.removeListener('close', onClose);
    src.removeListener('error', onError);
  }

  src.once('end', onEnd);
  src.once('close', onClose);
  src.once('error', onError);
};

module.exports = MockRequest;
<|endoftext|>


<commit_before>'use strict';

// Load modules

const Diff = require('diff');
const StringifySafe = require('json-stringify-safe');
const StableStringify = require('json-stable-stringify');

// Declare internals

const internals = {
    width: 50
};

internals.stringify = (obj, serializer, indent) => {

    return StableStringify(obj,
        { space: indent, replacer: StringifySafe.getSerialize(serializer) });
};

exports = module.exports = internals.Reporter = function (options) {

    this.settings = options;
    this.count = 0;
    this.last = [];

    this.colors = internals.colors(options.colors);
};

internals.Reporter.prototype.start = function (notebook) {

};

internals.Reporter.prototype.test = function (test) {

    if (this.settings.progress === 0) {
        return;
    }

    if (this.settings['silent-skips'] && (test.skipped || test.todo)) {
        return;
    }

    if (this.settings.progress === 1) {

        // ..x....x.-..

        if (!this.count) {
            this.report('\n  ');
        }

        this.count++;

        if ((this.count - 1) % internals.width === 0) {
            this.report('\n  ');
        }

        this.report(test.err ? this.colors.red('x') : (test.skipped || test.todo ? this.colors.magenta('-') : '.'));
    }
    else {

        // Can't be fully covereed but it's ok
        // $lab:coverage:off$
        const check = process.platform === 'win32' ? '\u221A' : '\u2714';
        const asterisk = process.platform === 'win32' ? '\u00D7' : '\u2716';
        // $lab:coverage:on$

        // Verbose (Spec reporter)

        for (let i = 0; i < test.path.length; ++i) {
            if (test.path[i] !== this.last[i] || (i > 0 && test.path[i - 1] !== this.last[i - 1])) {
                this.report(internals.spacer(i * 2) + test.path[i] + '\n');
            }
        }

        this.last = test.path;

        const spacer = internals.spacer(test.path.length * 2);
        if (test.err) {
            this.report(spacer + this.colors.red(asterisk + ' ' + test.id + ') ' + test.relativeTitle) + '\n');
        }
        else {
            const symbol = test.skipped || test.todo ? this.colors.magenta('-') : this.colors.green(check);
            const assertion = test.assertions === undefined ? '' : ' and ' + test.assertions + ' assertions';
            this.report(spacer + symbol + ' ' + this.colors.gray(test.id + ') ' + test.relativeTitle +
                ' (' + test.duration + ' ms' + assertion + ')') + '\n');
        }
    }
};

internals.spacer = function (length) {

    return new Array(length + 1).join(' ');
};

internals.stringifyReplacer = function (key, value) {

    // Show usually invisible values from JSON.stringify in a different way,
    // follow the bracket format of json-stringify-safe.

    if (value === undefined) {
        return '[undefined]';
    }

    if (typeof value === 'function' || value === Infinity || value === -Infinity) {
        return '[' + value.toString() + ']';
    }

    /* $lab:coverage:off$ */ // There is no way to cover that in node 0.10
    if (typeof value === 'symbol') {
        return '[' + value.toString() + ']';
    }
    /* $lab:coverage:on$ */

    return value;
};

internals.Reporter.prototype.end = function (notebook) {

    if (this.settings.progress) {
        this.report('\n\n');
    }

    // Colors

    const red = this.colors.red;
    const green = this.colors.green;
    const gray = this.colors.gray;
    const yellow = this.colors.yellow;
    const whiteRedBg = this.colors.whiteRedBg;
    const blackGreenBg = this.colors.blackGreenBg;

    // Tests

    const notes = notebook.tests.filter(internals.filterNotes);

    const failures = notebook.tests.filter(internals.filterFailures);

    const skipped = notebook.tests.filter(internals.filterSkipped);

    let output = '';
    const totalTests = notebook.tests.length - skipped.length;

    const errors = notebook.errors || [];
    if (errors.length) {
        output += 'Test script errors:\n\n';
        errors.forEach((err = {}) => {

            output += red(err.message) + '\n';
            if (err.stack) {
                const stack = err.stack.slice(err.stack.indexOf('\n') + 1)
                    .replace(/^/gm, '  ')
                    .split('\n')
                    .filter(internals.filterNodeModules)               // Remove node_modules files
                    .slice(0, 5)                                       // Show only first 5 stack lines
                    .join('\n');

                output += gray(stack) + '\n';
            }

            output += '\n';
        });
        output += red('There were ' + errors.length + ' test script error(s).') + '\n\n';
    }

    if (failures.length) {
        output += 'Failed tests:\n\n';

        for (let i = 0; i < failures.length; ++i) {
            const test = failures[i];
            const message = test.err.message || '';

            output += '  ' + test.id + ') ' + test.title + ':\n\n';

            // Actual vs Expected

            if (test.err.actual !== undefined &&
                test.err.expected !== undefined) {

                const actual = internals.stringify(test.err.actual, internals.stringifyReplacer, 2);
                const expected = internals.stringify(test.err.expected, internals.stringifyReplacer, 2);

                output += '      ' + whiteRedBg('actual') + ' ' + blackGreenBg('expected') + '\n\n      ';

                const comparison = Diff.diffWords(actual, expected);
                for (let j = 0; j < comparison.length; ++j) {
                    const item = comparison[j];
                    const value = item.value;
                    const lines = value.split('\n');
                    for (let k = 0; k < lines.length; ++k) {
                        if (k) {
                            output += '\n      ';
                        }

                        if (item.added || item.removed) {
                            output += item.added ? blackGreenBg(lines[k]) : whiteRedBg(lines[k]);
                        }
                        else {
                            output += lines[k];
                        }
                    }
                }

                output += '\n\n      ' + yellow(message);
                output += '\n\n';
            }
            else {
                output += '      ' + red(message) + '\n\n';
            }

            if (test.err.at) {
                output += gray('      at ' + test.err.at.filename + ':' + test.err.at.line + ':' + test.err.at.column) + '\n';
            }
            else if (!test.timeout &&
                test.err.stack) {

                output += gray(test.err.stack.slice(test.err.stack.indexOf('\n') + 1).replace(/^/gm, '  ')) + '\n';
            }

            if (test.err.data) {
                const isObject = typeof test.err.data === 'object' && !Array.isArray(test.err.data);
                let errorData = internals.stringify(test.err.data, null, isObject ? 4 : null);
                if (isObject) {
                    errorData = errorData.replace(/(\n\s*)"(.*)"\:/g, '$1$2:').split('\n').slice(1, -1).join('\n');
                }

                output += gray('\n      Additional error data:\n' + errorData.replace(/^/gm, '      ')) + '\n';
            }

            output += '\n';
        }

        output += '\n' + red(failures.length + ' of ' + totalTests + ' tests failed');
    }
    else {
        output += green(totalTests + ' tests complete');
    }

    if (skipped.length) {
        output += gray(` (${skipped.length} skipped)`);
    }

    output += '\n';
    output += 'Test duration: ' + notebook.ms + ' ms\n';

    // Assertions

    if (notebook.assertions !== undefined) {
        output += 'Assertions count: ' + notebook.assertions + ' (verbosity: ' + (notebook.assertions / totalTests).toFixed(2) + ')\n';
    }

    // Leaks

    if (notebook.leaks) {
        if (notebook.leaks.length) {
            output += red('The following leaks were detected:' + notebook.leaks.join(', ')) + '\n';
        }
        else {
            output += green('No global variable leaks detected') + '\n';
        }
    }

    if (notebook.shuffle) {
        output += 'Randomized with seed: ' + notebook.seed + '. Use --shuffle --seed ' + notebook.seed + ' to run tests in same order again.\n';
    }

    // Coverage

    const coverage = notebook.coverage;
    if (coverage) {
        const status = 'Coverage: ' + coverage.percent.toFixed(2) + '%';

        output += coverage.percent === 100 ? green(status) : red(status + ' (' + (coverage.sloc - coverage.hits) + '/' + coverage.sloc + ')');
        if (coverage.percent < 100) {
            coverage.files.forEach((file) => {

                let missingLines;
                if (file.sourcemaps) {
                    const missingLinesByFile = {};
                    Object.keys(file.source).forEach((lineNumber) => {

                        const line = file.source[lineNumber];
                        if (line.miss) {
                            missingLines = missingLinesByFile[line.originalFilename] = missingLinesByFile[line.originalFilename] || [];
                            missingLines.push(line.originalLine);
                        }
                    });

                    const files = Object.keys(missingLinesByFile);
                    if (files.length) {
                        output += red('\n' + file.filename + ' missing coverage from file(s):');
                        files.forEach((filename) => {

                            output += red('\n\t' + filename + ' on line(s): ' + missingLinesByFile[filename].join(', '));
                        });
                    }
                }
                else {
                    missingLines = [];
                    Object.keys(file.source).forEach((lineNumber) => {

                        const line = file.source[lineNumber];
                        if (line.miss) {
                            missingLines.push(lineNumber);
                        }
                    });

                    if (missingLines.length) {
                        output += red('\n' + file.filename + ' missing coverage on line(s): ' + missingLines.join(', '));
                    }
                }
            });

            if (coverage.percent < this.settings.threshold || isNaN(coverage.percent)) {
                output += red('\nCode coverage below threshold: ' + coverage.percent.toFixed(2) + ' < ' + this.settings.threshold);
            }
        }

        output += '\n';
    }

    const lint = notebook.lint;
    if (lint) {
        output += 'Linting results:';

        let hasErrors = false;
        lint.lint.forEach((entry) => {

            // Don't show anything if there aren't issues
            if (!entry.errors || !entry.errors.length) {
                return;
            }

            hasErrors = true;
            output += gray('\n\t' + entry.filename + ':');
            entry.errors.forEach((err) => {

                output += (err.severity === 'ERROR' ? red : yellow)('\n\t\tLine ' + err.line + ': ' + err.message);
            });
        });

        if (!hasErrors) {
            output += green(' No issues\n');
        }
    }

    if (notes.length) {
        output += '\n\nTest notes:\n';
        notes.forEach((test) => {

            output += gray(test.relativeTitle) + '\n';
            test.notes.forEach((note) => {

                output += yellow(`\t* ${note}`);
            });
        });
    }

    output += '\n';
    this.report(output);
};

internals.color = function (name, code, enabled) {

    if (enabled && Array.isArray(code)) {
        const color = '\u001b[' + code[0] + ';' + code[1] + 'm';
        return function (text) {

            return color + text + '\u001b[0m';
        };
    }
    else if (enabled) {
        const color = '\u001b[' + code + 'm';
        return function (text) {

            return color + text + '\u001b[0m';
        };
    }

    return function (text) {

        return text;
    };
};

internals.colors = function (enabled) {

    if (enabled === null) {
        enabled = require('supports-color');
    }

    const codes = {
        'black': 0,
        'gray': 92,
        'red': 31,
        'green': 32,
        'yellow': 33,
        'magenta': 35,
        'redBg': 41,
        'greenBg': 42,
        'whiteRedBg': [37, 41],
        'blackGreenBg': [30, 42]
    };

    const colors = {};
    const names = Object.keys(codes);
    for (let i = 0; i < names.length; ++i) {
        const name = names[i];
        colors[name] = internals.color(name, codes[name], enabled);
    }

    return colors;
};

internals.filterNotes = function (test) {

    return test.notes && test.notes.length;
};

internals.filterFailures = function (test) {

    return !!test.err;
};

internals.filterNodeModules = function (line) {

    return !(/\/node_modules\//.test(line));
};

internals.filterSkipped = function (test) {

    return test.skipped || test.todo;
};
<commit_msg>Fix trimming the stack message if it contains newlines<commit_after>'use strict';

// Load modules

const Diff = require('diff');
const StringifySafe = require('json-stringify-safe');
const StableStringify = require('json-stable-stringify');

// Declare internals

const internals = {
    width: 50
};

internals.stringify = (obj, serializer, indent) => {

    return StableStringify(obj,
        { space: indent, replacer: StringifySafe.getSerialize(serializer) });
};

exports = module.exports = internals.Reporter = function (options) {

    this.settings = options;
    this.count = 0;
    this.last = [];

    this.colors = internals.colors(options.colors);
};

internals.Reporter.prototype.start = function (notebook) {

};

internals.Reporter.prototype.test = function (test) {

    if (this.settings.progress === 0) {
        return;
    }

    if (this.settings['silent-skips'] && (test.skipped || test.todo)) {
        return;
    }

    if (this.settings.progress === 1) {

        // ..x....x.-..

        if (!this.count) {
            this.report('\n  ');
        }

        this.count++;

        if ((this.count - 1) % internals.width === 0) {
            this.report('\n  ');
        }

        this.report(test.err ? this.colors.red('x') : (test.skipped || test.todo ? this.colors.magenta('-') : '.'));
    }
    else {

        // Can't be fully covereed but it's ok
        // $lab:coverage:off$
        const check = process.platform === 'win32' ? '\u221A' : '\u2714';
        const asterisk = process.platform === 'win32' ? '\u00D7' : '\u2716';
        // $lab:coverage:on$

        // Verbose (Spec reporter)

        for (let i = 0; i < test.path.length; ++i) {
            if (test.path[i] !== this.last[i] || (i > 0 && test.path[i - 1] !== this.last[i - 1])) {
                this.report(internals.spacer(i * 2) + test.path[i] + '\n');
            }
        }

        this.last = test.path;

        const spacer = internals.spacer(test.path.length * 2);
        if (test.err) {
            this.report(spacer + this.colors.red(asterisk + ' ' + test.id + ') ' + test.relativeTitle) + '\n');
        }
        else {
            const symbol = test.skipped || test.todo ? this.colors.magenta('-') : this.colors.green(check);
            const assertion = test.assertions === undefined ? '' : ' and ' + test.assertions + ' assertions';
            this.report(spacer + symbol + ' ' + this.colors.gray(test.id + ') ' + test.relativeTitle +
                ' (' + test.duration + ' ms' + assertion + ')') + '\n');
        }
    }
};

internals.spacer = function (length) {

    return new Array(length + 1).join(' ');
};

internals.stringifyReplacer = function (key, value) {

    // Show usually invisible values from JSON.stringify in a different way,
    // follow the bracket format of json-stringify-safe.

    if (value === undefined) {
        return '[undefined]';
    }

    if (typeof value === 'function' || value === Infinity || value === -Infinity) {
        return '[' + value.toString() + ']';
    }

    /* $lab:coverage:off$ */ // There is no way to cover that in node 0.10
    if (typeof value === 'symbol') {
        return '[' + value.toString() + ']';
    }
    /* $lab:coverage:on$ */

    return value;
};

internals.Reporter.prototype.end = function (notebook) {

    if (this.settings.progress) {
        this.report('\n\n');
    }

    // Colors

    const red = this.colors.red;
    const green = this.colors.green;
    const gray = this.colors.gray;
    const yellow = this.colors.yellow;
    const whiteRedBg = this.colors.whiteRedBg;
    const blackGreenBg = this.colors.blackGreenBg;

    // Tests

    const notes = notebook.tests.filter(internals.filterNotes);

    const failures = notebook.tests.filter(internals.filterFailures);

    const skipped = notebook.tests.filter(internals.filterSkipped);

    let output = '';
    const totalTests = notebook.tests.length - skipped.length;

    const errors = notebook.errors || [];
    if (errors.length) {
        output += 'Test script errors:\n\n';
        errors.forEach((err = {}) => {

            output += red(err.message) + '\n';
            if (err.stack) {
                const stack = err.stack.slice(err.stack.indexOf('\n') + 1)
                    .replace(/^/gm, '  ')
                    .split('\n')
                    .filter(internals.filterNodeModules)               // Remove node_modules files
                    .slice(0, 5)                                       // Show only first 5 stack lines
                    .join('\n');

                output += gray(stack) + '\n';
            }

            output += '\n';
        });
        output += red('There were ' + errors.length + ' test script error(s).') + '\n\n';
    }

    if (failures.length) {
        output += 'Failed tests:\n\n';

        for (let i = 0; i < failures.length; ++i) {
            const test = failures[i];
            const message = test.err.message || '';

            output += '  ' + test.id + ') ' + test.title + ':\n\n';

            // Actual vs Expected

            if (test.err.actual !== undefined &&
                test.err.expected !== undefined) {

                const actual = internals.stringify(test.err.actual, internals.stringifyReplacer, 2);
                const expected = internals.stringify(test.err.expected, internals.stringifyReplacer, 2);

                output += '      ' + whiteRedBg('actual') + ' ' + blackGreenBg('expected') + '\n\n      ';

                const comparison = Diff.diffWords(actual, expected);
                for (let j = 0; j < comparison.length; ++j) {
                    const item = comparison[j];
                    const value = item.value;
                    const lines = value.split('\n');
                    for (let k = 0; k < lines.length; ++k) {
                        if (k) {
                            output += '\n      ';
                        }

                        if (item.added || item.removed) {
                            output += item.added ? blackGreenBg(lines[k]) : whiteRedBg(lines[k]);
                        }
                        else {
                            output += lines[k];
                        }
                    }
                }

                output += '\n\n      ' + yellow(message);
                output += '\n\n';
            }
            else {
                output += '      ' + red(message) + '\n\n';
            }

            if (test.err.at) {
                output += gray('      at ' + test.err.at.filename + ':' + test.err.at.line + ':' + test.err.at.column) + '\n';
            }
            else if (!test.timeout &&
                test.err.stack) {
                let stack = test.err.stack;
                if (stack.indexOf(message) !== -1) {
                    stack = stack.slice(stack.indexOf(message) + message.length + 1);
                }

                output += gray(stack.replace(/^/gm, '  ')) + '\n';
            }

            if (test.err.data) {
                const isObject = typeof test.err.data === 'object' && !Array.isArray(test.err.data);
                let errorData = internals.stringify(test.err.data, null, isObject ? 4 : null);
                if (isObject) {
                    errorData = errorData.replace(/(\n\s*)"(.*)"\:/g, '$1$2:').split('\n').slice(1, -1).join('\n');
                }

                output += gray('\n      Additional error data:\n' + errorData.replace(/^/gm, '      ')) + '\n';
            }

            output += '\n';
        }

        output += '\n' + red(failures.length + ' of ' + totalTests + ' tests failed');
    }
    else {
        output += green(totalTests + ' tests complete');
    }

    if (skipped.length) {
        output += gray(` (${skipped.length} skipped)`);
    }

    output += '\n';
    output += 'Test duration: ' + notebook.ms + ' ms\n';

    // Assertions

    if (notebook.assertions !== undefined) {
        output += 'Assertions count: ' + notebook.assertions + ' (verbosity: ' + (notebook.assertions / totalTests).toFixed(2) + ')\n';
    }

    // Leaks

    if (notebook.leaks) {
        if (notebook.leaks.length) {
            output += red('The following leaks were detected:' + notebook.leaks.join(', ')) + '\n';
        }
        else {
            output += green('No global variable leaks detected') + '\n';
        }
    }

    if (notebook.shuffle) {
        output += 'Randomized with seed: ' + notebook.seed + '. Use --shuffle --seed ' + notebook.seed + ' to run tests in same order again.\n';
    }

    // Coverage

    const coverage = notebook.coverage;
    if (coverage) {
        const status = 'Coverage: ' + coverage.percent.toFixed(2) + '%';

        output += coverage.percent === 100 ? green(status) : red(status + ' (' + (coverage.sloc - coverage.hits) + '/' + coverage.sloc + ')');
        if (coverage.percent < 100) {
            coverage.files.forEach((file) => {

                let missingLines;
                if (file.sourcemaps) {
                    const missingLinesByFile = {};
                    Object.keys(file.source).forEach((lineNumber) => {

                        const line = file.source[lineNumber];
                        if (line.miss) {
                            missingLines = missingLinesByFile[line.originalFilename] = missingLinesByFile[line.originalFilename] || [];
                            missingLines.push(line.originalLine);
                        }
                    });

                    const files = Object.keys(missingLinesByFile);
                    if (files.length) {
                        output += red('\n' + file.filename + ' missing coverage from file(s):');
                        files.forEach((filename) => {

                            output += red('\n\t' + filename + ' on line(s): ' + missingLinesByFile[filename].join(', '));
                        });
                    }
                }
                else {
                    missingLines = [];
                    Object.keys(file.source).forEach((lineNumber) => {

                        const line = file.source[lineNumber];
                        if (line.miss) {
                            missingLines.push(lineNumber);
                        }
                    });

                    if (missingLines.length) {
                        output += red('\n' + file.filename + ' missing coverage on line(s): ' + missingLines.join(', '));
                    }
                }
            });

            if (coverage.percent < this.settings.threshold || isNaN(coverage.percent)) {
                output += red('\nCode coverage below threshold: ' + coverage.percent.toFixed(2) + ' < ' + this.settings.threshold);
            }
        }

        output += '\n';
    }

    const lint = notebook.lint;
    if (lint) {
        output += 'Linting results:';

        let hasErrors = false;
        lint.lint.forEach((entry) => {

            // Don't show anything if there aren't issues
            if (!entry.errors || !entry.errors.length) {
                return;
            }

            hasErrors = true;
            output += gray('\n\t' + entry.filename + ':');
            entry.errors.forEach((err) => {

                output += (err.severity === 'ERROR' ? red : yellow)('\n\t\tLine ' + err.line + ': ' + err.message);
            });
        });

        if (!hasErrors) {
            output += green(' No issues\n');
        }
    }

    if (notes.length) {
        output += '\n\nTest notes:\n';
        notes.forEach((test) => {

            output += gray(test.relativeTitle) + '\n';
            test.notes.forEach((note) => {

                output += yellow(`\t* ${note}`);
            });
        });
    }

    output += '\n';
    this.report(output);
};

internals.color = function (name, code, enabled) {

    if (enabled && Array.isArray(code)) {
        const color = '\u001b[' + code[0] + ';' + code[1] + 'm';
        return function (text) {

            return color + text + '\u001b[0m';
        };
    }
    else if (enabled) {
        const color = '\u001b[' + code + 'm';
        return function (text) {

            return color + text + '\u001b[0m';
        };
    }

    return function (text) {

        return text;
    };
};

internals.colors = function (enabled) {

    if (enabled === null) {
        enabled = require('supports-color');
    }

    const codes = {
        'black': 0,
        'gray': 92,
        'red': 31,
        'green': 32,
        'yellow': 33,
        'magenta': 35,
        'redBg': 41,
        'greenBg': 42,
        'whiteRedBg': [37, 41],
        'blackGreenBg': [30, 42]
    };

    const colors = {};
    const names = Object.keys(codes);
    for (let i = 0; i < names.length; ++i) {
        const name = names[i];
        colors[name] = internals.color(name, codes[name], enabled);
    }

    return colors;
};

internals.filterNotes = function (test) {

    return test.notes && test.notes.length;
};

internals.filterFailures = function (test) {

    return !!test.err;
};

internals.filterNodeModules = function (line) {

    return !(/\/node_modules\//.test(line));
};

internals.filterSkipped = function (test) {

    return test.skipped || test.todo;
};
<|endoftext|>