Ts all the tests (#1998)

* Convert tests to TypeScript

* Add types for tests

* Rename files to TS

* Fix up test imports

* Fix TSC errors

* Liiiint

* Add types to util method signatures

* Some more types
This commit is contained in:
Paulus Schoutsen 2018-11-06 10:09:09 +01:00 committed by GitHub
parent 856ef34964
commit cdb2093ea6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
78 changed files with 524 additions and 364 deletions

View File

@ -8,8 +8,8 @@
"version": "1.0.0",
"scripts": {
"build": "script/build_frontend",
"lint": "eslint src hassio/src gallery/src test-mocha && tslint -c tslint.json 'src/**/*.ts' 'hassio/src/**/*.ts' 'gallery/src/**/*.ts' 'test-mocha/**/*.ts' && polymer lint && tsc",
"mocha": "node_modules/.bin/mocha --opts test-mocha/mocha.opts",
"lint": "eslint src hassio/src gallery/src && tslint 'src/**/*.ts' 'hassio/src/**/*.ts' 'gallery/src/**/*.ts' 'test-mocha/**/*.ts' && polymer lint && tsc",
"mocha": "node_modules/.bin/ts-mocha -p test-mocha/tsconfig.test.json --opts test-mocha/mocha.opts",
"test": "npm run lint && npm run mocha",
"docker_build": "sh ./script/docker_run.sh build $npm_package_version",
"bash": "sh ./script/docker_run.sh bash $npm_package_version"
@ -100,10 +100,12 @@
"@babel/preset-env": "^7.1.0",
"@babel/preset-typescript": "^7.1.0",
"@gfx/zopfli": "^1.0.9",
"@types/chai": "^4.1.7",
"@types/mocha": "^5.2.5",
"babel-eslint": "^10",
"babel-loader": "^8.0.4",
"babel-minify-webpack-plugin": "^0.3.1",
"chai": "^4.1.2",
"chai": "^4.2.0",
"compression-webpack-plugin": "^2.0.0",
"copy-webpack-plugin": "^4.5.2",
"del": "^3.0.0",
@ -126,7 +128,6 @@
"husky": "^1.1.0",
"lint-staged": "^8.0.2",
"merge-stream": "^1.0.1",
"mocha": "^5.2.0",
"parse5": "^5.1.0",
"polymer-analyzer": "^3.1.2",
"polymer-bundler": "^4.0.2",
@ -135,7 +136,8 @@
"raw-loader": "^0.5.1",
"reify": "^0.18.1",
"require-dir": "^1.0.0",
"sinon": "^7.1.0",
"sinon": "^7.1.1",
"ts-mocha": "^2.0.0",
"tslint": "^5.11.0",
"tslint-config-prettier": "^1.15.0",
"tslint-eslint-rules": "^5.4.0",

View File

@ -1,4 +0,0 @@
/** Return if a component is loaded. */
export default function isComponentLoaded(hass, component) {
return hass && hass.config.components.indexOf(component) !== -1;
}

View File

@ -0,0 +1,9 @@
import { HomeAssistant } from "../../types";
/** Return if a component is loaded. */
export default function isComponentLoaded(
hass: HomeAssistant,
component: string
): boolean {
return hass && hass.config.components.indexOf(component) !== -1;
}

View File

@ -1,4 +1,4 @@
/** Return if the displaymode is in standalone mode (PWA). */
export default function isPwa() {
export default function isPwa(): boolean {
return window.matchMedia("(display-mode: standalone)").matches;
}

View File

@ -1,4 +0,0 @@
/** Get the location name from a hass object. */
export default function computeLocationName(hass) {
return hass && hass.config.location_name;
}

View File

@ -0,0 +1,6 @@
import { HomeAssistant } from "../../types";
/** Get the location name from a hass object. */
export default function computeLocationName(hass: HomeAssistant): string {
return hass && hass.config.location_name;
}

View File

@ -1,4 +1,4 @@
export default function durationToSeconds(duration) {
export default function durationToSeconds(duration: string): number {
const parts = duration.split(":").map(Number);
return parts[0] * 3600 + parts[1] * 60 + parts[2];
}

View File

@ -1,4 +1,4 @@
import fecha from "fecha";
import * as fecha from "fecha";
// Check for support of native locale string options
function toLocaleDateStringSupportsOptions() {
@ -11,11 +11,10 @@ function toLocaleDateStringSupportsOptions() {
}
export default (toLocaleDateStringSupportsOptions()
? (dateObj, locales) =>
? (dateObj: Date, locales: string) =>
dateObj.toLocaleDateString(locales, {
year: "numeric",
month: "long",
day: "numeric",
})
: // eslint-disable-next-line no-unused-vars
(dateObj, locales) => fecha.format(dateObj, "mediumDate"));
: (dateObj: Date) => fecha.format(dateObj, "mediumDate"));

View File

@ -1,4 +1,4 @@
import fecha from "fecha";
import * as fecha from "fecha";
// Check for support of native locale string options
function toLocaleStringSupportsOptions() {
@ -11,7 +11,7 @@ function toLocaleStringSupportsOptions() {
}
export default (toLocaleStringSupportsOptions()
? (dateObj, locales) =>
? (dateObj: Date, locales: string) =>
dateObj.toLocaleString(locales, {
year: "numeric",
month: "long",
@ -19,5 +19,4 @@ export default (toLocaleStringSupportsOptions()
hour: "numeric",
minute: "2-digit",
})
: // eslint-disable-next-line no-unused-vars
(dateObj, locales) => fecha.format(dateObj, "haDateTime"));
: (dateObj: Date) => fecha.format(dateObj, "haDateTime"));

View File

@ -1,4 +1,4 @@
import fecha from "fecha";
import * as fecha from "fecha";
// Check for support of native locale string options
function toLocaleTimeStringSupportsOptions() {
@ -11,10 +11,9 @@ function toLocaleTimeStringSupportsOptions() {
}
export default (toLocaleTimeStringSupportsOptions()
? (dateObj, locales) =>
? (dateObj: Date, locales: string) =>
dateObj.toLocaleTimeString(locales, {
hour: "numeric",
minute: "2-digit",
})
: // eslint-disable-next-line no-unused-vars
(dateObj, locales) => fecha.format(dateObj, "shortTime"));
: (dateObj: Date) => fecha.format(dateObj, "shortTime"));

View File

@ -1,33 +0,0 @@
/** Calculate a string representing a date object as relative time from now.
*
* Example output: 5 minutes ago, in 3 days.
*/
const tests = [60, "second", 60, "minute", 24, "hour", 7, "day"];
export default function relativeTime(dateObj, localize) {
let delta = (new Date() - dateObj) / 1000;
const tense = delta >= 0 ? "past" : "future";
delta = Math.abs(delta);
for (let i = 0; i < tests.length; i += 2) {
if (delta < tests[i]) {
delta = Math.floor(delta);
const time = localize(
`ui.components.relative_time.duration.${tests[i + 1]}`,
"count",
delta
);
return localize(`ui.components.relative_time.${tense}`, "time", time);
}
delta /= tests[i];
}
delta = Math.floor(delta);
const time = localize(
"ui.components.relative_time.duration.week",
"count",
delta
);
return localize(`ui.components.relative_time.${tense}`, "time", time);
}

View File

@ -0,0 +1,40 @@
import { LocalizeFunc } from "../../mixins/localize-base-mixin";
/**
* Calculate a string representing a date object as relative time from now.
*
* Example output: 5 minutes ago, in 3 days.
*/
const tests = [60, 60, 24, 7];
const langKey = ["second", "minute", "hour", "day"];
export default function relativeTime(
dateObj: Date,
localize: LocalizeFunc
): string {
let delta = (new Date().getTime() - dateObj.getTime()) / 1000;
const tense = delta >= 0 ? "past" : "future";
delta = Math.abs(delta);
for (let i = 0; i < tests.length; i++) {
if (delta < tests[i]) {
delta = Math.floor(delta);
const timeDesc = localize(
`ui.components.relative_time.duration.${langKey[i]}`,
"count",
delta
);
return localize(`ui.components.relative_time.${tense}`, "time", timeDesc);
}
delta /= tests[i];
}
delta = Math.floor(delta);
const time = localize(
"ui.components.relative_time.duration.week",
"count",
delta
);
return localize(`ui.components.relative_time.${tense}`, "time", time);
}

View File

@ -1,6 +1,6 @@
const leftPad = (number) => (number < 10 ? `0${number}` : number);
const leftPad = (num: number) => (num < 10 ? `0${num}` : num);
export default function secondsToDuration(d) {
export default function secondsToDuration(d: number) {
const h = Math.floor(d / 3600);
const m = Math.floor((d % 3600) / 60);
const s = Math.floor((d % 3600) % 60);

View File

@ -1,9 +0,0 @@
export default function attributeClassNames(stateObj, attributes) {
if (!stateObj) return "";
return attributes
.map(function(attribute) {
return attribute in stateObj.attributes ? "has-" + attribute : "";
})
.filter((attr) => attr !== "")
.join(" ");
}

View File

@ -0,0 +1,17 @@
import { HassEntity } from "home-assistant-js-websocket";
export default function attributeClassNames(
stateObj: HassEntity,
attributes: string[]
): string {
if (!stateObj) {
return "";
}
return attributes
.map(
(attribute) =>
attribute in stateObj.attributes ? "has-" + attribute : ""
)
.filter((attr) => attr !== "")
.join(" ");
}

View File

@ -1,7 +1,9 @@
import { HassEntity } from "home-assistant-js-websocket";
/** Return an icon representing a binary sensor state. */
export default function binarySensorIcon(state) {
var activated = state.state && state.state === "off";
export default function binarySensorIcon(state: HassEntity) {
const activated = state.state && state.state === "off";
switch (state.attributes.device_class) {
case "battery":
return activated ? "hass:battery" : "hass:battery-outline";

View File

@ -1,4 +1,6 @@
export default function canToggleDomain(hass, domain) {
import { HomeAssistant } from "../../types";
export default function canToggleDomain(hass: HomeAssistant, domain: string) {
const services = hass.services[domain];
if (!services) {
return false;

View File

@ -1,13 +1,19 @@
import { HassEntity } from "home-assistant-js-websocket";
import canToggleDomain from "./can_toggle_domain";
import computeStateDomain from "./compute_state_domain";
import { HomeAssistant } from "../../types";
export default function canToggleState(hass, stateObj) {
export default function canToggleState(
hass: HomeAssistant,
stateObj: HassEntity
) {
const domain = computeStateDomain(stateObj);
if (domain === "group") {
return stateObj.state === "on" || stateObj.state === "off";
}
if (domain === "climate") {
return !!((stateObj.attributes || {}).supported_features & 4096);
// tslint:disable-next-line
return (stateObj.attributes.supported_features! & 4096) !== 0;
}
return canToggleDomain(hass, domain);

View File

@ -1,3 +0,0 @@
export default function computeDomain(entityId) {
return entityId.substr(0, entityId.indexOf("."));
}

View File

@ -0,0 +1,3 @@
export default function computeDomain(entityId: string): string {
return entityId.substr(0, entityId.indexOf("."));
}

View File

@ -1,4 +1,4 @@
/** Compute the object ID of a state. */
export default function computeObjectId(entityId) {
export default function computeObjectId(entityId: string): string {
return entityId.substr(entityId.indexOf(".") + 1);
}

View File

@ -1,84 +0,0 @@
import computeStateDomain from "./compute_state_domain";
import formatDateTime from "../datetime/format_date_time";
import formatDate from "../datetime/format_date";
import formatTime from "../datetime/format_time";
export default function computeStateDisplay(localize, stateObj, language) {
if (!stateObj._stateDisplay) {
const domain = computeStateDomain(stateObj);
if (domain === "binary_sensor") {
// Try device class translation, then default binary sensor translation
if (stateObj.attributes.device_class) {
stateObj._stateDisplay = localize(
`state.${domain}.${stateObj.attributes.device_class}.${
stateObj.state
}`
);
}
if (!stateObj._stateDisplay) {
stateObj._stateDisplay = localize(
`state.${domain}.default.${stateObj.state}`
);
}
} else if (
stateObj.attributes.unit_of_measurement &&
!["unknown", "unavailable"].includes(stateObj.state)
) {
stateObj._stateDisplay =
stateObj.state + " " + stateObj.attributes.unit_of_measurement;
} else if (domain === "input_datetime") {
let date;
if (!stateObj.attributes.has_time) {
date = new Date(
stateObj.attributes.year,
stateObj.attributes.month - 1,
stateObj.attributes.day
);
stateObj._stateDisplay = formatDate(date, language);
} else if (!stateObj.attributes.has_date) {
const now = new Date();
date = new Date(
// Due to bugs.chromium.org/p/chromium/issues/detail?id=797548
// don't use artificial 1970 year.
now.getFullYear(),
now.getMonth(),
now.getDay(),
stateObj.attributes.hour,
stateObj.attributes.minute
);
stateObj._stateDisplay = formatTime(date, language);
} else {
date = new Date(
stateObj.attributes.year,
stateObj.attributes.month - 1,
stateObj.attributes.day,
stateObj.attributes.hour,
stateObj.attributes.minute
);
stateObj._stateDisplay = formatDateTime(date, language);
}
} else if (domain === "zwave") {
if (["initializing", "dead"].includes(stateObj.state)) {
stateObj._stateDisplay = localize(
`state.zwave.query_stage.${stateObj.state}`,
"query_stage",
stateObj.attributes.query_stage
);
} else {
stateObj._stateDisplay = localize(
`state.zwave.default.${stateObj.state}`
);
}
} else {
stateObj._stateDisplay = localize(`state.${domain}.${stateObj.state}`);
}
// Fall back to default, component backend translation, or raw state if nothing else matches.
stateObj._stateDisplay =
stateObj._stateDisplay ||
localize(`state.default.${stateObj.state}`) ||
localize(`component.${domain}.state.${stateObj.state}`) ||
stateObj.state;
}
return stateObj._stateDisplay;
}

View File

@ -0,0 +1,91 @@
import { HassEntity } from "home-assistant-js-websocket";
import computeStateDomain from "./compute_state_domain";
import formatDateTime from "../datetime/format_date_time";
import formatDate from "../datetime/format_date";
import formatTime from "../datetime/format_time";
import { LocalizeFunc } from "../../mixins/localize-base-mixin";
type CachedDisplayEntity = HassEntity & {
_stateDisplay?: string;
};
export default function computeStateDisplay(
localize: LocalizeFunc,
stateObj: HassEntity,
language: string
) {
const state = stateObj as CachedDisplayEntity;
if (!state._stateDisplay) {
const domain = computeStateDomain(state);
if (domain === "binary_sensor") {
// Try device class translation, then default binary sensor translation
if (state.attributes.device_class) {
state._stateDisplay = localize(
`state.${domain}.${state.attributes.device_class}.${state.state}`
);
}
if (!state._stateDisplay) {
state._stateDisplay = localize(
`state.${domain}.default.${state.state}`
);
}
} else if (
state.attributes.unit_of_measurement &&
!["unknown", "unavailable"].includes(state.state)
) {
state._stateDisplay =
state.state + " " + state.attributes.unit_of_measurement;
} else if (domain === "input_datetime") {
let date;
if (!state.attributes.has_time) {
date = new Date(
state.attributes.year,
state.attributes.month - 1,
state.attributes.day
);
state._stateDisplay = formatDate(date, language);
} else if (!state.attributes.has_date) {
const now = new Date();
date = new Date(
// Due to bugs.chromium.org/p/chromium/issues/detail?id=797548
// don't use artificial 1970 year.
now.getFullYear(),
now.getMonth(),
now.getDay(),
state.attributes.hour,
state.attributes.minute
);
state._stateDisplay = formatTime(date, language);
} else {
date = new Date(
state.attributes.year,
state.attributes.month - 1,
state.attributes.day,
state.attributes.hour,
state.attributes.minute
);
state._stateDisplay = formatDateTime(date, language);
}
} else if (domain === "zwave") {
if (["initializing", "dead"].includes(state.state)) {
state._stateDisplay = localize(
`state.zwave.query_stage.${state.state}`,
"query_stage",
state.attributes.query_stage
);
} else {
state._stateDisplay = localize(`state.zwave.default.${state.state}`);
}
} else {
state._stateDisplay = localize(`state.${domain}.${state.state}`);
}
// Fall back to default, component backend translation, or raw state if nothing else matches.
state._stateDisplay =
state._stateDisplay ||
localize(`state.default.${state.state}`) ||
localize(`component.${domain}.state.${state.state}`) ||
state.state;
}
return state._stateDisplay;
}

View File

@ -1,5 +0,0 @@
import computeDomain from "./compute_domain";
export default function computeStateDomain(stateObj) {
return computeDomain(stateObj.entity_id);
}

View File

@ -0,0 +1,6 @@
import { HassEntity } from "home-assistant-js-websocket";
import computeDomain from "./compute_domain";
export default function computeStateDomain(stateObj: HassEntity) {
return computeDomain(stateObj.entity_id);
}

View File

@ -1,11 +0,0 @@
import computeObjectId from "./compute_object_id";
export default function computeStateName(stateObj) {
if (stateObj._entityDisplay === undefined) {
stateObj._entityDisplay =
stateObj.attributes.friendly_name ||
computeObjectId(stateObj.entity_id).replace(/_/g, " ");
}
return stateObj._entityDisplay;
}

View File

@ -0,0 +1,18 @@
import { HassEntity } from "home-assistant-js-websocket";
import computeObjectId from "./compute_object_id";
type CachedDisplayEntity = HassEntity & {
_entityDisplay?: string;
};
export default function computeStateName(stateObj: HassEntity) {
const state = stateObj as CachedDisplayEntity;
if (state._entityDisplay === undefined) {
state._entityDisplay =
state.attributes.friendly_name ||
computeObjectId(state.entity_id).replace(/_/g, " ");
}
return state._entityDisplay;
}

View File

@ -1,8 +1,9 @@
/** Return an icon representing a cover state. */
import { HassEntity } from "home-assistant-js-websocket";
import domainIcon from "./domain_icon";
export default function coverIcon(state) {
var open = state.state && state.state !== "closed";
export default function coverIcon(state: HassEntity): string {
const open = state.state !== "closed";
switch (state.attributes.device_class) {
case "garage":
return open ? "hass:garage-open" : "hass:garage";

View File

@ -44,7 +44,7 @@ const fixedIcons = {
weblink: "hass:open-in-new",
};
export default function domainIcon(domain, state) {
export default function domainIcon(domain: string, state?: string): string {
if (domain in fixedIcons) {
return fixedIcons[domain];
}
@ -93,11 +93,10 @@ export default function domainIcon(domain, state) {
}
default:
/* eslint-disable no-console */
// tslint:disable-next-line
console.warn(
"Unable to find icon for domain " + domain + " (" + state + ")"
);
/* eslint-enable no-console */
return DEFAULT_DOMAIN_ICON;
}
}

View File

@ -1,8 +1,9 @@
import { HassEntities, HassEntity } from "home-assistant-js-websocket";
import { DEFAULT_VIEW_ENTITY_ID } from "../const";
// Return an ordered array of available views
export default function extractViews(entities) {
const views = [];
export default function extractViews(entities: HassEntities): HassEntity[] {
const views: HassEntity[] = [];
Object.keys(entities).forEach((entityId) => {
const entity = entities[entityId];

View File

@ -1,11 +0,0 @@
// Expects classNames to be an object mapping feature-bit -> className
export default function featureClassNames(stateObj, classNames) {
if (!stateObj || !stateObj.attributes.supported_features) return "";
const features = stateObj.attributes.supported_features;
return Object.keys(classNames)
.map((feature) => ((features & feature) !== 0 ? classNames[feature] : ""))
.filter((attr) => attr !== "")
.join(" ");
}

View File

@ -0,0 +1,22 @@
import { HassEntity } from "home-assistant-js-websocket";
// Expects classNames to be an object mapping feature-bit -> className
export default function featureClassNames(
stateObj: HassEntity,
classNames: { [feature: number]: string }
) {
if (!stateObj || !stateObj.attributes.supported_features) {
return "";
}
const features = stateObj.attributes.supported_features;
return Object.keys(classNames)
.map(
(feature) =>
// tslint:disable-next-line
(features & Number(feature)) !== 0 ? classNames[feature] : ""
)
.filter((attr) => attr !== "")
.join(" ");
}

View File

@ -1,4 +1,10 @@
export default function getGroupEntities(entities, group) {
import { HassEntities } from "home-assistant-js-websocket";
import { GroupEntity } from "../../types";
export default function getGroupEntities(
entities: HassEntities,
group: GroupEntity
) {
const result = {};
group.attributes.entity_id.forEach((entityId) => {

View File

@ -1,9 +1,14 @@
import { HassEntities } from "home-assistant-js-websocket";
import computeDomain from "./compute_domain";
import getGroupEntities from "./get_group_entities";
import { GroupEntity } from "../../types";
// Return an object containing all entities that the view will show
// including embedded groups.
export default function getViewEntities(entities, view) {
export default function getViewEntities(
entities: HassEntities,
view: GroupEntity
) {
const viewEntities = {};
view.attributes.entity_id.forEach((entityId) => {
@ -13,7 +18,7 @@ export default function getViewEntities(entities, view) {
viewEntities[entity.entity_id] = entity;
if (computeDomain(entity.entity_id) === "group") {
const groupEntities = getGroupEntities(entities, entity);
const groupEntities = getGroupEntities(entities, entity as GroupEntity);
Object.keys(groupEntities).forEach((grEntityId) => {
const grEntity = groupEntities[grEntityId];

View File

@ -1,5 +0,0 @@
export default function hasLocation(stateObj) {
return (
"latitude" in stateObj.attributes && "longitude" in stateObj.attributes
);
}

View File

@ -0,0 +1,7 @@
import { HassEntity } from "home-assistant-js-websocket";
export default function hasLocation(stateObj: HassEntity) {
return (
"latitude" in stateObj.attributes && "longitude" in stateObj.attributes
);
}

View File

@ -1,7 +1,8 @@
/** Return an icon representing an input datetime state. */
import domainIcon from "./domain_icon";
import { HassEntity } from "home-assistant-js-websocket";
export default function inputDateTimeIcon(state) {
export default function inputDateTimeIcon(state: HassEntity): string {
if (!state.attributes.has_date) {
return "hass:clock";
}

View File

@ -1,4 +1,5 @@
/** Return an icon representing a sensor state. */
import { HassEntity } from "home-assistant-js-websocket";
import { UNIT_C, UNIT_F } from "../const";
import domainIcon from "./domain_icon";
@ -9,17 +10,18 @@ const fixedDeviceClassIcons = {
pressure: "hass:gauge",
};
export default function sensorIcon(state) {
export default function sensorIcon(state: HassEntity) {
const dclass = state.attributes.device_class;
if (dclass in fixedDeviceClassIcons) {
if (dclass && dclass in fixedDeviceClassIcons) {
return fixedDeviceClassIcons[dclass];
}
if (dclass === "battery") {
if (isNaN(state.state)) {
const battery = Number(state.state);
if (isNaN(battery)) {
return "hass:battery-unknown";
}
const batteryRound = Math.round(state.state / 10) * 10;
const batteryRound = Math.round(battery / 10) * 10;
if (batteryRound >= 100) {
return "hass:battery";
}

View File

@ -1,11 +1,12 @@
import computeDomain from "./compute_domain";
import { HassEntity, HassEntities } from "home-assistant-js-websocket";
// Split a collection into a list of groups and a 'rest' list of ungrouped
// entities.
// Returns { groups: [], ungrouped: {} }
export default function splitByGroups(entities) {
const groups = [];
const ungrouped = {};
export default function splitByGroups(entities: HassEntities) {
const groups: HassEntity[] = [];
const ungrouped: HassEntities = {};
Object.keys(entities).forEach((entityId) => {
const entity = entities[entityId];

View File

@ -1,8 +1,13 @@
import { HassEntity } from "home-assistant-js-websocket";
import canToggleState from "./can_toggle_state";
import computeStateDomain from "./compute_state_domain";
import { DOMAINS_WITH_CARD } from "../const";
import { HomeAssistant } from "../../types";
export default function stateCardType(hass, stateObj) {
export default function stateCardType(
hass: HomeAssistant,
stateObj: HassEntity
) {
if (stateObj.state === "unavailable") {
return "display";
}

View File

@ -1,4 +1,5 @@
/** Return an icon representing a state. */
import { HassEntity } from "home-assistant-js-websocket";
import { DEFAULT_DOMAIN_ICON } from "../const";
import computeDomain from "./compute_domain";
@ -16,7 +17,7 @@ const domainIcons = {
input_datetime: inputDateTimeIcon,
};
export default function stateIcon(state) {
export default function stateIcon(state: HassEntity) {
if (!state) {
return DEFAULT_DOMAIN_ICON;
}

View File

@ -1,7 +1,8 @@
import { HassEntity } from "home-assistant-js-websocket";
import computeStateDomain from "./compute_state_domain";
import { DOMAINS_HIDE_MORE_INFO, DOMAINS_WITH_MORE_INFO } from "../const";
export default function stateMoreInfoType(stateObj) {
export default function stateMoreInfoType(stateObj: HassEntity) {
const domain = computeStateDomain(stateObj);
if (DOMAINS_WITH_MORE_INFO.includes(domain)) {

View File

@ -5,9 +5,13 @@
* const states = [state1, state2]
* states.sort(statesSortByName);
*/
import { HassEntity } from "home-assistant-js-websocket";
import computeStateName from "./compute_state_name";
export default function sortStatesByName(entityA, entityB) {
export default function sortStatesByName(
entityA: HassEntity,
entityB: HassEntity
) {
const nameA = computeStateName(entityA);
const nameB = computeStateName(entityB);
if (nameA < nameB) {

View File

@ -1,11 +1,12 @@
import { HassEntity } from "home-assistant-js-websocket";
import durationToSeconds from "../datetime/duration_to_seconds";
export default function timerTimeRemaining(stateObj) {
export default function timerTimeRemaining(stateObj: HassEntity) {
let timeRemaining = durationToSeconds(stateObj.attributes.remaining);
if (stateObj.state === "active") {
const now = new Date();
const madeActive = new Date(stateObj.last_changed);
const now = new Date().getTime();
const madeActive = new Date(stateObj.last_changed).getTime();
timeRemaining = Math.max(timeRemaining - (now - madeActive) / 1000, 0);
}

View File

@ -1,3 +0,0 @@
export default function validEntityId(entityId) {
return /^(\w+)\.(\w+)$/.test(entityId);
}

View File

@ -0,0 +1,2 @@
const validEntityId = /^(\w+)\.(\w+)$/;
export default validEntityId.test;

View File

@ -1,9 +1,11 @@
export default function parseAspectRatio(input) {
// Handle 16x9, 16:9, 1.78x1, 1.78:1, 1.78
// Ignore everything else
function parseOrThrow(number) {
const parsed = parseFloat(number);
if (isNaN(parsed)) throw new Error(`${number} is not a number`);
function parseOrThrow(num) {
const parsed = parseFloat(num);
if (isNaN(parsed)) {
throw new Error(`${num} is not a number`);
}
return parsed;
}
try {

View File

@ -211,7 +211,11 @@ export class HuiGlanceCard extends hassLocalizeLitMixin(LitElement)
></state-badge>
${
this._config!.show_state !== false
? html`<div>${computeStateDisplay(this.localize, stateObj)}</div>`
? html`<div>${computeStateDisplay(
this.localize,
stateObj,
this.hass!.language
)}</div>`
: ""
}
</div>

View File

@ -48,7 +48,9 @@ class HuiStateLabelElement extends hassLocalizeLitMixin(LitElement)
.longPress="${longPress()}"
>
${this._config.prefix}${
state ? computeStateDisplay(this.localize, state) : "-"
state
? computeStateDisplay(this.localize, state, this.hass!.language)
: "-"
}${this._config.suffix}
</div>
`;

View File

@ -6,6 +6,7 @@ import {
MessageBase,
HassEntityBase,
HassEntityAttributeBase,
HassServices,
} from "home-assistant-js-websocket";
declare global {
@ -75,6 +76,7 @@ export interface HomeAssistant {
connection: Connection;
connected: boolean;
states: HassEntities;
services: HassServices;
config: HassConfig;
themes: Themes;
panels: Panels;
@ -141,3 +143,13 @@ export type LightEntity = HassEntityBase & {
hs_color: number[];
};
};
export type GroupEntity = HassEntityBase & {
attributes: HassEntityAttributeBase & {
entity_id: string[];
order: number;
auto?: boolean;
view?: boolean;
control?: "hidden";
};
};

View File

@ -6,12 +6,12 @@ describe("attributeClassNames", () => {
const attrs = ["mock_attr1", "mock_attr2"];
it("Skips null states", () => {
const stateObj = null;
const stateObj: any = null;
assert.strictEqual(attributeClassNames(stateObj, attrs), "");
});
it("Matches no attrbutes", () => {
const stateObj = {
const stateObj: any = {
attributes: {
other_attr_1: 1,
other_attr_2: 2,
@ -21,7 +21,7 @@ describe("attributeClassNames", () => {
});
it("Matches one attrbute", () => {
const stateObj = {
const stateObj: any = {
attributes: {
other_attr_1: 1,
other_attr_2: 2,
@ -32,7 +32,7 @@ describe("attributeClassNames", () => {
});
it("Matches two attrbutes", () => {
const stateObj = {
const stateObj: any = {
attributes: {
other_attr_1: 1,
other_attr_2: 2,

View File

@ -3,7 +3,7 @@ import { assert } from "chai";
import canToggleDomain from "../../../src/common/entity/can_toggle_domain";
describe("canToggleDomain", () => {
const hass = {
const hass: any = {
services: {
light: {
turn_on: null, // Service keys only need to be present for test

View File

@ -3,7 +3,7 @@ import { assert } from "chai";
import canToggleState from "../../../src/common/entity/can_toggle_state";
describe("canToggleState", () => {
const hass = {
const hass: any = {
services: {
light: {
turn_on: null, // Service keys only need to be present for test
@ -13,7 +13,7 @@ describe("canToggleState", () => {
};
it("Detects lights toggle", () => {
const stateObj = {
const stateObj: any = {
entity_id: "light.bla",
state: "on",
};
@ -21,7 +21,7 @@ describe("canToggleState", () => {
});
it("Detects group with toggle", () => {
const stateObj = {
const stateObj: any = {
entity_id: "group.bla",
state: "on",
};
@ -29,7 +29,7 @@ describe("canToggleState", () => {
});
it("Detects group without toggle", () => {
const stateObj = {
const stateObj: any = {
entity_id: "group.devices",
state: "home",
};
@ -37,7 +37,7 @@ describe("canToggleState", () => {
});
it("Detects climate with toggle", () => {
const stateObj = {
const stateObj: any = {
entity_id: "climate.bla",
attributes: {
supported_features: 4096,
@ -47,8 +47,11 @@ describe("canToggleState", () => {
});
it("Detects climate without toggle", () => {
const stateObj = {
const stateObj: any = {
entity_id: "climate.bla",
attributes: {
supported_features: 0,
},
};
assert.isFalse(canToggleState(hass, stateObj));
});

View File

@ -3,13 +3,12 @@ import { assert } from "chai";
import computeStateDisplay from "../../../src/common/entity/compute_state_display";
describe("computeStateDisplay", () => {
const localize = function(message, ...args) {
// Mock Localize function for testing
return message + (args.length ? ": " + args.join(",") : "");
};
const localize = (message, ...args) =>
message + (args.length ? ": " + args.join(",") : "");
it("Localizes binary sensor defaults", () => {
const stateObj = {
const stateObj: any = {
entity_id: "binary_sensor.test",
state: "off",
attributes: {},
@ -21,7 +20,7 @@ describe("computeStateDisplay", () => {
});
it("Localizes binary sensor device class", () => {
const stateObj = {
const stateObj: any = {
entity_id: "binary_sensor.test",
state: "off",
attributes: {
@ -35,12 +34,13 @@ describe("computeStateDisplay", () => {
});
it("Localizes binary sensor invalid device class", () => {
const altLocalize = function(message, ...args) {
if (message === "state.binary_sensor.invalid_device_class.off")
return null;
const altLocalize = (message, ...args) => {
if (message === "state.binary_sensor.invalid_device_class.off") {
return "";
}
return localize(message, ...args);
};
const stateObj = {
const stateObj: any = {
entity_id: "binary_sensor.test",
state: "off",
attributes: {
@ -54,7 +54,7 @@ describe("computeStateDisplay", () => {
});
it("Localizes sensor value with units", () => {
const stateObj = {
const stateObj: any = {
entity_id: "sensor.test",
state: "123",
attributes: {
@ -65,11 +65,13 @@ describe("computeStateDisplay", () => {
});
it("Localizes unknown sensor value with units", () => {
const altLocalize = function(message, ...args) {
if (message === "state.sensor.unknown") return null;
const altLocalize = (message, ...args) => {
if (message === "state.sensor.unknown") {
return "";
}
return localize(message, ...args);
};
const stateObj = {
const stateObj: any = {
entity_id: "sensor.test",
state: "unknown",
attributes: {
@ -83,11 +85,13 @@ describe("computeStateDisplay", () => {
});
it("Localizes unavailable sensor value with units", () => {
const altLocalize = function(message, ...args) {
if (message === "state.sensor.unavailable") return null;
const altLocalize = (message, ...args) => {
if (message === "state.sensor.unavailable") {
return "";
}
return localize(message, ...args);
};
const stateObj = {
const stateObj: any = {
entity_id: "sensor.test",
state: "unavailable",
attributes: {
@ -101,11 +105,13 @@ describe("computeStateDisplay", () => {
});
it("Localizes sensor value with component translation", () => {
const altLocalize = function(message, ...args) {
if (message !== "component.sensor.state.custom_state") return null;
const altLocalize = (message, ...args) => {
if (message !== "component.sensor.state.custom_state") {
return "";
}
return localize(message, ...args);
};
const stateObj = {
const stateObj: any = {
entity_id: "sensor.test",
state: "custom_state",
attributes: {},
@ -117,7 +123,7 @@ describe("computeStateDisplay", () => {
});
it("Localizes input_datetime with full date time", () => {
const stateObj = {
const stateObj: any = {
entity_id: "input_datetime.test",
state: "123",
attributes: {
@ -138,7 +144,7 @@ describe("computeStateDisplay", () => {
});
it("Localizes input_datetime with date", () => {
const stateObj = {
const stateObj: any = {
entity_id: "input_datetime.test",
state: "123",
attributes: {
@ -159,7 +165,7 @@ describe("computeStateDisplay", () => {
});
it("Localizes input_datetime with time", () => {
const stateObj = {
const stateObj: any = {
entity_id: "input_datetime.test",
state: "123",
attributes: {
@ -180,7 +186,7 @@ describe("computeStateDisplay", () => {
});
it("Localizes zwave ready", () => {
const stateObj = {
const stateObj: any = {
entity_id: "zwave.test",
state: "ready",
attributes: {
@ -194,7 +200,7 @@ describe("computeStateDisplay", () => {
});
it("Localizes zwave initializing", () => {
const stateObj = {
const stateObj: any = {
entity_id: "zwave.test",
state: "initializing",
attributes: {
@ -208,7 +214,7 @@ describe("computeStateDisplay", () => {
});
it("Localizes cover open", () => {
const stateObj = {
const stateObj: any = {
entity_id: "cover.test",
state: "open",
attributes: {},
@ -220,11 +226,13 @@ describe("computeStateDisplay", () => {
});
it("Localizes unavailable", () => {
const altLocalize = function(message, ...args) {
if (message === "state.sensor.unavailable") return null;
const altLocalize = (message, ...args) => {
if (message === "state.sensor.unavailable") {
return "";
}
return localize(message, ...args);
};
const stateObj = {
const stateObj: any = {
entity_id: "sensor.test",
state: "unavailable",
attributes: {},
@ -236,11 +244,11 @@ describe("computeStateDisplay", () => {
});
it("Localizes custom state", () => {
const altLocalize = function() {
const altLocalize = () => {
// No matches can be found
return null;
return "";
};
const stateObj = {
const stateObj: any = {
entity_id: "sensor.test",
state: "My Custom State",
attributes: {},
@ -252,7 +260,7 @@ describe("computeStateDisplay", () => {
});
it("Only calculates state display once per immutable state object", () => {
const stateObj = {
const stateObj: any = {
entity_id: "cover.test",
state: "open",
attributes: {},

View File

@ -4,7 +4,7 @@ import computeStateDomain from "../../../src/common/entity/compute_state_domain"
describe("computeStateDomain", () => {
it("Detects sensor domain", () => {
const stateObj = {
const stateObj: any = {
entity_id: "sensor.test",
};
assert.strictEqual(computeStateDomain(stateObj), "sensor");

View File

@ -1,4 +1,4 @@
import assert from "assert";
import * as assert from "assert";
import extractViews from "../../../src/common/entity/extract_views";

View File

@ -1,6 +1,7 @@
import { assert } from "chai";
import featureClassNames from "../../../src/common/entity/feature_class_names";
import { HassEntity } from "home-assistant-js-websocket";
describe("featureClassNames", () => {
const classNames = {
@ -12,11 +13,12 @@ describe("featureClassNames", () => {
it("Skips null states", () => {
const stateObj = null;
assert.strictEqual(featureClassNames(stateObj, classNames), "");
assert.strictEqual(featureClassNames(stateObj!, classNames), "");
});
it("Matches no features", () => {
const stateObj = {
// tslint:disable-next-line
const stateObj = <HassEntity>{
attributes: {
supported_features: 64,
},
@ -25,7 +27,8 @@ describe("featureClassNames", () => {
});
it("Matches one feature", () => {
const stateObj = {
// tslint:disable-next-line
const stateObj = <HassEntity>{
attributes: {
supported_features: 72,
},
@ -37,7 +40,8 @@ describe("featureClassNames", () => {
});
it("Matches two features", () => {
const stateObj = {
// tslint:disable-next-line
const stateObj = <HassEntity>{
attributes: {
supported_features: 73,
},

View File

@ -1,4 +1,4 @@
import assert from "assert";
import * as assert from "assert";
import getGroupEntities from "../../../src/common/entity/get_group_entities";

View File

@ -1,4 +1,4 @@
import assert from "assert";
import * as assert from "assert";
import getViewEntities from "../../../src/common/entity/get_view_entities";

View File

@ -4,7 +4,7 @@ import hasLocation from "../../../src/common/entity/has_location";
describe("hasLocation", () => {
it("flags states with location", () => {
const stateObj = {
const stateObj: any = {
attributes: {
latitude: 12.34,
longitude: 12.34,
@ -13,7 +13,7 @@ describe("hasLocation", () => {
assert(hasLocation(stateObj));
});
it("does not flag states with only latitude", () => {
const stateObj = {
const stateObj: any = {
attributes: {
latitude: 12.34,
},
@ -21,7 +21,7 @@ describe("hasLocation", () => {
assert(!hasLocation(stateObj));
});
it("does not flag states with only longitude", () => {
const stateObj = {
const stateObj: any = {
attributes: {
longitude: 12.34,
},
@ -29,7 +29,7 @@ describe("hasLocation", () => {
assert(!hasLocation(stateObj));
});
it("does not flag states with no location", () => {
const stateObj = {
const stateObj: any = {
attributes: {},
};
assert(!hasLocation(stateObj));

View File

@ -1,4 +1,4 @@
import assert from "assert";
import * as assert from "assert";
import splitByGroups from "../../../src/common/entity/split_by_groups";

View File

@ -3,7 +3,7 @@ import { assert } from "chai";
import stateCardType from "../../../src/common/entity/state_card_type";
describe("stateCardType", () => {
const hass = {
const hass: any = {
services: {
light: {
turn_on: null, // Service keys only need to be present for test
@ -13,21 +13,21 @@ describe("stateCardType", () => {
};
it("Returns display for unavailable states", () => {
const stateObj = {
const stateObj: any = {
state: "unavailable",
};
assert.strictEqual(stateCardType(hass, stateObj), "display");
});
it("Returns media_player for media_player states", () => {
const stateObj = {
const stateObj: any = {
entity_id: "media_player.bla",
};
assert.strictEqual(stateCardType(hass, stateObj), "media_player");
});
it("Returns toggle for states that can toggle", () => {
const stateObj = {
const stateObj: any = {
entity_id: "light.bla",
attributes: {},
};
@ -35,7 +35,7 @@ describe("stateCardType", () => {
});
it("Returns display for states with hidden control", () => {
const stateObj = {
const stateObj: any = {
entity_id: "light.bla",
attributes: {
control: "hidden",
@ -45,7 +45,7 @@ describe("stateCardType", () => {
});
it("Returns display for entities that cannot toggle", () => {
const stateObj = {
const stateObj: any = {
entity_id: "sensor.bla",
};
assert.strictEqual(stateCardType(hass, stateObj), "display");

View File

@ -4,14 +4,14 @@ import stateMoreInfoType from "../../../src/common/entity/state_more_info_type";
describe("stateMoreInfoType", () => {
it("Returns media_player for media_player states", () => {
const stateObj = {
const stateObj: any = {
entity_id: "media_player.bla",
};
assert.strictEqual(stateMoreInfoType(stateObj), "media_player");
});
it("Returns hidden for input_select states", () => {
const stateObj = {
const stateObj: any = {
entity_id: "input_select.bla",
attributes: {},
};
@ -19,7 +19,7 @@ describe("stateMoreInfoType", () => {
});
it("Returns default for switch states", () => {
const stateObj = {
const stateObj: any = {
entity_id: "switch.bla",
attributes: {},
};

View File

@ -27,7 +27,7 @@ export function createView(entity) {
return createGroup(entity);
}
export function createLightEntity(isOn) {
export function createLightEntity(isOn?) {
mockState++;
if (isOn === undefined) {
isOn = Math.random() > 0.5;

View File

@ -1,5 +1,5 @@
import { assert } from "chai";
import sinon from "sinon";
import * as sinon from "sinon";
import timerTimeRemaining from "../../../src/common/entity/timer_time_remaining";
@ -11,7 +11,7 @@ describe("timerTimeRemaining", () => {
attributes: {
remaining: "0:01:05",
},
}),
} as any),
65
);
});
@ -23,7 +23,7 @@ describe("timerTimeRemaining", () => {
attributes: {
remaining: "0:01:05",
},
}),
} as any),
65
);
});
@ -44,7 +44,7 @@ describe("timerTimeRemaining", () => {
remaining: "0:01:05",
},
last_changed: "2018-01-17T16:15:12Z",
}),
} as any),
47
);
});

View File

@ -1,4 +1,4 @@
import assert from "assert";
import * as assert from "assert";
import parseAspectRatio from "../../../src/common/util/parse-aspect-ratio";

View File

@ -1,4 +1,3 @@
--recursive
--require reify
--timeout 10000
test-mocha
test-mocha/**/*.ts

View File

@ -0,0 +1,6 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"module": "commonjs"
}
}

6
test-mocha/tslint.json Normal file
View File

@ -0,0 +1,6 @@
{
"extends": ["../tslint.json"],
"rules": {
"no-implicit-dependencies": [true, "dev"]
}
}

150
yarn.lock
View File

@ -1372,10 +1372,10 @@
dependencies:
any-observable "^0.3.0"
"@sinonjs/commons@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.0.2.tgz#3e0ac737781627b8844257fadc3d803997d0526e"
integrity sha512-WR3dlgqJP4QNrLC4iXN/5/2WaLQQ0VijOOkmflqFGVJ6wLEpbSjo7c0ZeGIdtY8Crk7xBBp87sM6+Mkerz7alw==
"@sinonjs/commons@^1.2.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.3.0.tgz#50a2754016b6f30a994ceda6d9a0a8c36adda849"
integrity sha512-j4ZwhaHmwsCb4DlDOIWnI5YyKDNMoNThsmwEpfHx6a1EpsGZ9qYLxP++LMlmBRjtGptGHFsGItJ768snllFWpA==
dependencies:
type-detect "4.0.8"
@ -1449,7 +1449,7 @@
dependencies:
"@types/chai" "*"
"@types/chai@*":
"@types/chai@*", "@types/chai@^4.1.7":
version "4.1.7"
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.1.7.tgz#1b8e33b61a8c09cbe1f85133071baa0dbf9fa71a"
integrity sha512-2Y8uPt0/jwjhQ6EiluT0XCri1Dbplr0ZxfFXUz+ye13gaqE8u5gL5ppao1JrUYr9cIip5S6MvQzBS7Kke7U9VA==
@ -1624,6 +1624,11 @@
resolved "https://registry.yarnpkg.com/@types/is-windows/-/is-windows-0.2.0.tgz#6f24ee48731d31168ea510610d6dd15e5fc9c6ff"
integrity sha1-byTuSHMdMRaOpRBhDW3RXl/Jxv8=
"@types/json5@^0.0.29":
version "0.0.29"
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
"@types/launchpad@^0.6.0":
version "0.6.0"
resolved "https://registry.yarnpkg.com/@types/launchpad/-/launchpad-0.6.0.tgz#37296109b7f277f6e6c5fd7e0c0706bc918fbb51"
@ -1646,6 +1651,11 @@
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
"@types/mocha@^5.2.5":
version "5.2.5"
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.5.tgz#8a4accfc403c124a0bafe8a9fc61a05ec1032073"
integrity sha512-lAVp+Kj54ui/vLUFxsJTMtWvZraZxum3w3Nwkble2dNuV5VnPA+Mi2oGX9XYJAaIvZi3tn3cbjS/qcJXRb6Bww==
"@types/mz@0.0.29":
version "0.0.29"
resolved "https://registry.yarnpkg.com/@types/mz/-/mz-0.0.29.tgz#bc24728c649973f1c7851e9033f9ce525668c27b"
@ -3667,11 +3677,6 @@ browser-stdout@1.3.0:
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f"
integrity sha1-81HTKWnTL6XXpVZxVCY9korjvR8=
browser-stdout@1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
browserify-aes@^1.0.0, browserify-aes@^1.0.4:
version "1.2.0"
resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48"
@ -3777,7 +3782,7 @@ buffer-fill@^1.0.0:
resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
integrity sha1-+PeLdniYiO858gXNY39o5wISKyw=
buffer-from@^1.0.0:
buffer-from@^1.0.0, buffer-from@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
@ -3965,7 +3970,7 @@ chai@^3.5.0:
deep-eql "^0.1.3"
type-detect "^1.0.0"
chai@^4.1.2:
chai@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5"
integrity sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==
@ -4369,7 +4374,7 @@ command-line-usage@^5.0.5:
table-layout "^0.4.3"
typical "^2.6.1"
commander@2.15.1, commander@2.15.x, commander@~2.15.0:
commander@2.15.x, commander@~2.15.0:
version "2.15.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==
@ -4848,7 +4853,7 @@ debug@2.6.8:
dependencies:
ms "2.0.0"
debug@3.1.0, debug@=3.1.0, debug@~3.1.0:
debug@=3.1.0, debug@~3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
@ -4939,6 +4944,11 @@ deep-is@~0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
deepmerge@^2.0.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170"
integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==
default-gateway@^2.6.0:
version "2.7.2"
resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-2.7.2.tgz#b7ef339e5e024b045467af403d50348db4642d0f"
@ -5118,16 +5128,16 @@ diff@3.2.0:
resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9"
integrity sha1-yc45Okt8vQsFinJck98pkCeGj/k=
diff@3.5.0, diff@^3.1.0, diff@^3.2.0, diff@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
diff@^2.1.2:
version "2.2.3"
resolved "https://registry.yarnpkg.com/diff/-/diff-2.2.3.tgz#60eafd0d28ee906e4e8ff0a52c1229521033bf99"
integrity sha1-YOr9DSjukG5Oj/ClLBIpUhAzv5k=
diff@^3.1.0, diff@^3.2.0, diff@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
diffie-hellman@^5.0.0:
version "5.0.3"
resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
@ -6772,18 +6782,6 @@ glob@7.1.1:
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@7.1.2:
version "7.1.2"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^4.3.1:
version "4.5.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-4.5.3.tgz#c6cb73d3226c1efef04de3c56d012f03377ee15f"
@ -7065,11 +7063,6 @@ grouped-queue@^0.3.0, grouped-queue@^0.3.3:
dependencies:
lodash "^4.17.2"
growl@1.10.5:
version "1.10.5"
resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==
growl@1.9.2:
version "1.9.2"
resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f"
@ -9334,6 +9327,11 @@ make-dir@^1.0.0, make-dir@^1.1.0:
dependencies:
pify "^3.0.0"
make-error@^1.1.1:
version "1.3.5"
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8"
integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==
map-age-cleaner@^0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.2.tgz#098fb15538fd3dbe461f12745b0ca8568d4e3f74"
@ -9599,7 +9597,7 @@ minimatch-all@^1.1.0:
dependencies:
minimatch "^3.0.2"
"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4:
"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
@ -9721,23 +9719,6 @@ mocha@^3.4.2:
mkdirp "0.5.1"
supports-color "3.1.2"
mocha@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6"
integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==
dependencies:
browser-stdout "1.3.1"
commander "2.15.1"
debug "3.1.0"
diff "3.5.0"
escape-string-regexp "1.0.5"
glob "7.1.2"
growl "1.10.5"
he "1.1.1"
minimatch "3.0.4"
mkdirp "0.5.1"
supports-color "5.4.0"
moment@^2.10.2, moment@^2.22.0:
version "2.22.1"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.1.tgz#529a2e9bf973f259c9643d237fda84de3a26e8ad"
@ -12321,12 +12302,12 @@ sinon@^2.3.5:
text-encoding "0.6.4"
type-detect "^4.0.0"
sinon@^7.1.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/sinon/-/sinon-7.1.0.tgz#819b63002ee09a90a3b50a0da4e0bdecb2e3f345"
integrity sha512-ffASxced8xr8eU0EGyfj9K++bRCtv/NyOFOxl7UBD86YH97oZjVxvecMhObwRlXe27GRUa6rVFEn67khPZ29rQ==
sinon@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/sinon/-/sinon-7.1.1.tgz#1202f317aa14d93cb9b69ff50b6bd49c0e05ffc9"
integrity sha512-iYagtjLVt1vN3zZY7D8oH7dkjNJEjLjyuzy8daX5+3bbQl8gaohrheB9VfH1O3L6LKuue5WTJvFluHiuZ9y3nQ==
dependencies:
"@sinonjs/commons" "^1.0.2"
"@sinonjs/commons" "^1.2.0"
"@sinonjs/formatio" "^3.0.0"
"@sinonjs/samsam" "^2.1.2"
diff "^3.5.0"
@ -12503,6 +12484,14 @@ source-map-support@^0.4.15:
dependencies:
source-map "^0.5.6"
source-map-support@^0.5.6:
version "0.5.9"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.9.tgz#41bc953b2534267ea2d605bccfa7bfa3111ced5f"
integrity sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==
dependencies:
buffer-from "^1.0.0"
source-map "^0.6.0"
source-map-url@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
@ -12513,7 +12502,7 @@ source-map@0.5.x, source-map@^0.5.0, source-map@^0.5.6, source-map@^0.5.7, sourc
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
@ -12875,7 +12864,7 @@ strip-json-comments@^2.0.1, strip-json-comments@~2.0.1:
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
supports-color@3.1.2, supports-color@5.4.0, supports-color@^0.2.0, supports-color@^2.0.0, supports-color@^5.1.0, supports-color@^5.3.0, supports-color@^5.4.0, supports-color@^5.5.0:
supports-color@3.1.2, supports-color@^0.2.0, supports-color@^2.0.0, supports-color@^5.1.0, supports-color@^5.3.0, supports-color@^5.4.0, supports-color@^5.5.0:
version "3.1.2"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5"
integrity sha1-cqJiiU2dQIuVbKBf83su2KbiotU=
@ -13251,6 +13240,40 @@ triple-beam@^1.2.0, triple-beam@^1.3.0:
resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9"
integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==
ts-mocha@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ts-mocha/-/ts-mocha-2.0.0.tgz#0dbd3cd04671df9933b9303b4aa46347573c5635"
integrity sha512-Rj6+vvwKtOTs5GsNO1jLl4DIXUGnyAg5HFt2Yb4SHIRN45clTJkHWpNdTxCSL0u+1oeavSYJah6d1PZ++Ju5pw==
dependencies:
ts-node "7.0.0"
optionalDependencies:
tsconfig-paths "^3.5.0"
ts-node@7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-7.0.0.tgz#a94a13c75e5e1aa6b82814b84c68deb339ba7bff"
integrity sha512-klJsfswHP0FuOLsvBZ/zzCfUvakOSSxds78mVeK7I+qP76YWtxf16hEZsp3U+b0kIo82R5UatGFeblYMqabb2Q==
dependencies:
arrify "^1.0.0"
buffer-from "^1.1.0"
diff "^3.1.0"
make-error "^1.1.1"
minimist "^1.2.0"
mkdirp "^0.5.1"
source-map-support "^0.5.6"
yn "^2.0.0"
tsconfig-paths@^3.5.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.6.0.tgz#f14078630d9d6e8b1dc690c1fc0cfb9cd0663891"
integrity sha512-mrqQIP2F4e03aMTCiPdedCIT300//+q0ET53o5WqqtQjmEICxP9yfz/sHTpPqXpssuJEzODsEzJaLRaf5J2X1g==
dependencies:
"@types/json5" "^0.0.29"
deepmerge "^2.0.1"
json5 "^1.0.1"
minimist "^1.2.0"
strip-bom "^3.0.0"
tslib@1.9.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8"
@ -14645,6 +14668,11 @@ yeoman-generator@^3.1.1:
through2 "^2.0.0"
yeoman-environment "^2.0.5"
yn@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a"
integrity sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=
zip-stream@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-1.2.0.tgz#a8bc45f4c1b49699c6b90198baacaacdbcd4ba04"