getValorImplicito(ent: Ambito, arbol: AST) {
        if (this.operador !== Operador.MENOS_UNARIO && this.operador !== Operador.NOT && this.operador !== Operador.NODO){
            let op1 = this.op_izquierda.getValorImplicito(ent, arbol);
            let op2 = this.op_derecha.getValorImplicito(ent, arbol);

            //suma
            if (this.operador == Operador.SUMA)
            {
                if (typeof(op1==="number") && typeof(op2==="number"))
                {
                    return op1 + op2;
                }
                else if (op1==="string" || op2 ==="string")
                {
                    if (op1 == null) op1 = "null";
                    if (op2 == null) op2 = "null";
                    return op1.ToString() + op2.ToString();
                }
                else
                {
                    console.log("Error de tipos de datos no permitidos realizando una suma");
                    return null;
                }
            }
            //resta
            else if (this.operador == Operador.RESTA)
            {
                if (typeof(op1==="number") && typeof(op2==="number"))
                {
                    return op1 - op2;
                }
                else
                {
                    console.log("Error de tipos de datos no permitidos realizando una suma");
                    return null;
                }
            }
            //multiplicación
            else if (this.operador == Operador.MULTIPLICACION)
            {
                if (typeof(op1==="number") && typeof(op2==="number"))
                {
                    return op1 * op2;
                }
                else
                {
                    console.log("Error de tipos de datos no permitidos realizando una suma");
                    return null;
                }
            }
            //division
            else if (this.operador == Operador.DIVISION)
            {
                if (typeof(op1==="number") && typeof(op2==="number"))
                {
                    if(op2===0){
                        console.log("Resultado indefinido, no puede ejecutarse operación sobre cero.");
                        return null;
                    }
                    return op1 / op2;
                }
                else
                {
                    console.log("Error de tipos de datos no permitidos realizando una suma");
                    return null;
                }
            }
            //modulo
            else if (this.operador == Operador.MODULO)
            {
                if (typeof(op1==="number") && typeof(op2==="number"))
                {
                    if(op2===0){
                        console.log("Resultado indefinido, no puede ejecutarse operación sobre cero.");
                        return null;
                    }
                    return op1 % op2;
                }
                else
                {
                    console.log("Error de tipos de datos no permitidos realizando una suma");
                    return null;
                }
            }

        }else{
            let op1 = this.op_izquierda.getValorImplicito(ent, arbol);
            if (this.operador == Operador.NODO)
            {
                return op1;
            }
            else if (this.operador == Operador.MENOS_UNARIO)
            {
                if (typeof(op1==="number"))
                {
                    return -1* op1;
                }
                else
                {
                    console.log("Error de tipos de datos no permitidos realizando una operación unaria");
                    return null;
                }
            }
        }
        return null;
    }


isInt(n:number){
        return Number(n) === n && n % 1 === 0;
    }


() => {
            this.setState({ itemCount: this.todoListService.getItemCount() });
        }


() => {
            this.setState({ listItems: this.todoListService.getListItems() });
        }


($event) => this.addAndClearItem($event)


(item) => {
                            return <div>{item}</div>
                        }


export default class App extends Component<any, AppState> {
    @lazyInject('TODOListService') todoListService: TODOListService;

    // Creates a reference to our html input element so that we can get its value
    inputElement = createRef<HTMLInputElement>();

    constructor() {
        super();
        this.state = {
            itemCount: this.todoListService.getItemCount(),
            listItems: this.todoListService.getListItems() || [],
        }

        // When event an item is added to the todoList, this will be notified of it.
        window.addEventListener(LIST_CHANGED_EVENT_TYPE_ID, () => {
            this.setState({ itemCount: this.todoListService.getItemCount() });
        });

        window.addEventListener(LIST_CHANGED_EVENT_TYPE_ID, () => {
            this.setState({ listItems: this.todoListService.getListItems() });
        });
    }

    addAndClearItem($event) {
        const item = this.inputElement.current.value;
        if (item && item.length > 0) {
            this.todoListService.addListItem(item);
        }
        this.inputElement.current.value = "";
    }

    render() {
        return (

                    You have {this.state.itemCount} items todo.

                        <input ref={this.inputElement} type="text" />
                        <button onClick={($event) => this.addAndClearItem($event)}>Add</button>

                    {
                        // Render current list
                        this.state.listItems.map((item) => {
                            return
{item}

                        })
                    }

        );
    }
}


interface AppState {
    itemCount: number;
    listItems: string[];
}


addAndClearItem($event) {
        const item = this.inputElement.current.value;
        if (item && item.length > 0) {
            this.todoListService.addListItem(item);
        }
        this.inputElement.current.value = "";
    }


render() {
        return (

                    You have {this.state.itemCount} items todo.

                        <input ref={this.inputElement} type="text" />
                        <button onClick={($event) => this.addAndClearItem($event)}>Add</button>

                    {
                        // Render current list
                        this.state.listItems.map((item) => {
                            return
{item}

                        })
                    }

        );
    }


function createProtocolError(error: ProtocolError, method: string, protocolError: { message: string; data: any; }): ProtocolError {
  let message = `Protocol error (${method}): ${protocolError.message}`;
  if ('data' in protocolError)
    message += ` ${protocolError.data}`;
  return rewriteErrorMessage(error, message);
}


() => this.emit(ConnectionEvents.Disconnected)


(resolve, reject) => {
      this._callbacks.set(id, { resolve, reject, error: new ProtocolError(false), method });
    }


(error: ProtocolError) => debugLogger.log('error', error)


() => {
        if (this._eventListener)
          this._eventListener(object.method!, object.params);
        this.emit(object.method!, object.params);
      }


() => this.emit(CRSessionEvents.Disconnected)


export class CRConnection extends EventEmitter {
  private _lastId = 0;
  private readonly _transport: ConnectionTransport;
  private readonly _sessions = new Map<string, CRSession>();
  private readonly _protocolLogger: ProtocolLogger;
  private readonly _browserLogsCollector: RecentLogsCollector;
  readonly rootSession: CRSession;
  _closed = false;

  constructor(transport: ConnectionTransport, protocolLogger: ProtocolLogger, browserLogsCollector: RecentLogsCollector) {
    super();
    this.setMaxListeners(0);
    this._transport = transport;
    this._protocolLogger = protocolLogger;
    this._browserLogsCollector = browserLogsCollector;
    this._transport.onmessage = this._onMessage.bind(this);
    this._transport.onclose = this._onClose.bind(this);
    this.rootSession = new CRSession(this, '', 'browser', '');
    this._sessions.set('', this.rootSession);
  }

  static fromSession(session: CRSession): CRConnection {
    return session._connection!;
  }

  session(sessionId: string): CRSession | null {
    return this._sessions.get(sessionId) || null;
  }

  _rawSend(sessionId: string, method: string, params: any): number {
    const id = ++this._lastId;
    const message: ProtocolRequest = { id, method, params };
    if (sessionId)
      message.sessionId = sessionId;
    this._protocolLogger('send', message);
    this._transport.send(message);
    return id;
  }

  async _onMessage(message: ProtocolResponse) {
    this._protocolLogger('receive', message);
    if (message.id === kBrowserCloseMessageId)
      return;
    if (message.method === 'Target.attachedToTarget') {
      const sessionId = message.params.sessionId;
      const rootSessionId = message.sessionId || '';
      const session = new CRSession(this, rootSessionId, message.params.targetInfo.type, sessionId);
      this._sessions.set(sessionId, session);
    } else if (message.method === 'Target.detachedFromTarget') {
      const session = this._sessions.get(message.params.sessionId);
      if (session) {
        session._onClosed(undefined);
        this._sessions.delete(message.params.sessionId);
      }
    }
    const session = this._sessions.get(message.sessionId || '');
    if (session)
      session._onMessage(message);
  }

  _onClose() {
    this._closed = true;
    this._transport.onmessage = undefined;
    this._transport.onclose = undefined;
    const browserDisconnectedLogs = helper.formatBrowserLogs(this._browserLogsCollector.recentLogs());
    for (const session of this._sessions.values())
      session._onClosed(browserDisconnectedLogs);
    this._sessions.clear();
    Promise.resolve().then(() => this.emit(ConnectionEvents.Disconnected));
  }

  close() {
    if (!this._closed)
      this._transport.close();
  }

  async createSession(targetInfo: Protocol.Target.TargetInfo): Promise<CRSession> {
    const { sessionId } = await this.rootSession.send('Target.attachToTarget', { targetId: targetInfo.targetId, flatten: true });
    return this._sessions.get(sessionId)!;
  }

  async createBrowserSession(): Promise<CRSession> {
    const { sessionId } = await this.rootSession.send('Target.attachToBrowserTarget');
    return this._sessions.get(sessionId)!;
  }
}


export class CRSession extends EventEmitter {
  _connection: CRConnection | null;
  _eventListener?: (method: string, params?: Object) => void;
  private readonly _callbacks = new Map<number, {resolve: (o: any) => void, reject: (e: ProtocolError) => void, error: ProtocolError, method: string}>();
  private readonly _targetType: string;
  private readonly _sessionId: string;
  private readonly _rootSessionId: string;
  private _crashed: boolean = false;
  private _browserDisconnectedLogs: string | undefined;
  override on: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
  override addListener: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
  override off: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
  override removeListener: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
  override once: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
  readonly guid: string;

  constructor(connection: CRConnection, rootSessionId: string, targetType: string, sessionId: string) {
    super();
    this.guid = `cdp-session@${sessionId}`;
    this.setMaxListeners(0);
    this._connection = connection;
    this._rootSessionId = rootSessionId;
    this._targetType = targetType;
    this._sessionId = sessionId;

    this.on = super.on;
    this.addListener = super.addListener;
    this.off = super.removeListener;
    this.removeListener = super.removeListener;
    this.once = super.once;
  }

  _markAsCrashed() {
    this._crashed = true;
  }

  async send<T extends keyof Protocol.CommandParameters>(
    method: T,
    params?: Protocol.CommandParameters[T]
  ): Promise<Protocol.CommandReturnValues[T]> {
    if (this._crashed)
      throw new ProtocolError(true, 'Target crashed');
    if (this._browserDisconnectedLogs !== undefined)
      throw new ProtocolError(true, `Browser closed.` + this._browserDisconnectedLogs);
    if (!this._connection)
      throw new ProtocolError(true, `Target closed`);
    const id = this._connection._rawSend(this._sessionId, method, params);
    return new Promise((resolve, reject) => {
      this._callbacks.set(id, { resolve, reject, error: new ProtocolError(false), method });
    });
  }

  _sendMayFail<T extends keyof Protocol.CommandParameters>(method: T, params?: Protocol.CommandParameters[T]): Promise<Protocol.CommandReturnValues[T] | void> {
    return this.send(method, params).catch((error: ProtocolError) => debugLogger.log('error', error));
  }

  _onMessage(object: ProtocolResponse) {
    if (object.id && this._callbacks.has(object.id)) {
      const callback = this._callbacks.get(object.id)!;
      this._callbacks.delete(object.id);
      if (object.error)
        callback.reject(createProtocolError(callback.error, callback.method, object.error));
      else
        callback.resolve(object.result);
    } else if (object.id && object.error?.code === -32001) {
      // Message to a closed session, just ignore it.
    } else {
      assert(!object.id);
      Promise.resolve().then(() => {
        if (this._eventListener)
          this._eventListener(object.method!, object.params);
        this.emit(object.method!, object.params);
      });
    }
  }

  async detach() {
    if (!this._connection)
      throw new Error(`Session already detached. Most likely the ${this._targetType} has been closed.`);
    const rootSession = this._connection.session(this._rootSessionId);
    if (!rootSession)
      throw new Error('Root session has been closed');
    await rootSession.send('Target.detachFromTarget', { sessionId: this._sessionId });
  }

  _onClosed(browserDisconnectedLogs: string | undefined) {
    this._browserDisconnectedLogs = browserDisconnectedLogs;
    const errorMessage = browserDisconnectedLogs !== undefined ? 'Browser closed.' + browserDisconnectedLogs : 'Target closed';
    for (const callback of this._callbacks.values()) {
      callback.error.sessionClosed = true;
      callback.reject(rewriteErrorMessage(callback.error, errorMessage));
    }
    this._callbacks.clear();
    this._connection = null;
    Promise.resolve().then(() => this.emit(CRSessionEvents.Disconnected));
  }
}


static fromSession(session: CRSession): CRConnection {
    return session._connection!;
  }


session(sessionId: string): CRSession | null {
    return this._sessions.get(sessionId) || null;
  }


_rawSend(sessionId: string, method: string, params: any): number {
    const id = ++this._lastId;
    const message: ProtocolRequest = { id, method, params };
    if (sessionId)
      message.sessionId = sessionId;
    this._protocolLogger('send', message);
    this._transport.send(message);
    return id;
  }


async _onMessage(message: ProtocolResponse) {
    this._protocolLogger('receive', message);
    if (message.id === kBrowserCloseMessageId)
      return;
    if (message.method === 'Target.attachedToTarget') {
      const sessionId = message.params.sessionId;
      const rootSessionId = message.sessionId || '';
      const session = new CRSession(this, rootSessionId, message.params.targetInfo.type, sessionId);
      this._sessions.set(sessionId, session);
    } else if (message.method === 'Target.detachedFromTarget') {
      const session = this._sessions.get(message.params.sessionId);
      if (session) {
        session._onClosed(undefined);
        this._sessions.delete(message.params.sessionId);
      }
    }
    const session = this._sessions.get(message.sessionId || '');
    if (session)
      session._onMessage(message);
  }


_onClose() {
    this._closed = true;
    this._transport.onmessage = undefined;
    this._transport.onclose = undefined;
    const browserDisconnectedLogs = helper.formatBrowserLogs(this._browserLogsCollector.recentLogs());
    for (const session of this._sessions.values())
      session._onClosed(browserDisconnectedLogs);
    this._sessions.clear();
    Promise.resolve().then(() => this.emit(ConnectionEvents.Disconnected));
  }


close() {
    if (!this._closed)
      this._transport.close();
  }


async createSession(targetInfo: Protocol.Target.TargetInfo): Promise<CRSession> {
    const { sessionId } = await this.rootSession.send('Target.attachToTarget', { targetId: targetInfo.targetId, flatten: true });
    return this._sessions.get(sessionId)!;
  }


async createBrowserSession(): Promise<CRSession> {
    const { sessionId } = await this.rootSession.send('Target.attachToBrowserTarget');
    return this._sessions.get(sessionId)!;
  }


_markAsCrashed() {
    this._crashed = true;
  }


async send<T extends keyof Protocol.CommandParameters>(
    method: T,
    params?: Protocol.CommandParameters[T]
  ): Promise<Protocol.CommandReturnValues[T]> {
    if (this._crashed)
      throw new ProtocolError(true, 'Target crashed');
    if (this._browserDisconnectedLogs !== undefined)
      throw new ProtocolError(true, `Browser closed.` + this._browserDisconnectedLogs);
    if (!this._connection)
      throw new ProtocolError(true, `Target closed`);
    const id = this._connection._rawSend(this._sessionId, method, params);
    return new Promise((resolve, reject) => {
      this._callbacks.set(id, { resolve, reject, error: new ProtocolError(false), method });
    });
  }


_sendMayFail<T extends keyof Protocol.CommandParameters>(method: T, params?: Protocol.CommandParameters[T]): Promise<Protocol.CommandReturnValues[T] | void> {
    return this.send(method, params).catch((error: ProtocolError) => debugLogger.log('error', error));
  }


_onMessage(object: ProtocolResponse) {
    if (object.id && this._callbacks.has(object.id)) {
      const callback = this._callbacks.get(object.id)!;
      this._callbacks.delete(object.id);
      if (object.error)
        callback.reject(createProtocolError(callback.error, callback.method, object.error));
      else
        callback.resolve(object.result);
    } else if (object.id && object.error?.code === -32001) {
      // Message to a closed session, just ignore it.
    } else {
      assert(!object.id);
      Promise.resolve().then(() => {
        if (this._eventListener)
          this._eventListener(object.method!, object.params);
        this.emit(object.method!, object.params);
      });
    }
  }


async detach() {
    if (!this._connection)
      throw new Error(`Session already detached. Most likely the ${this._targetType} has been closed.`);
    const rootSession = this._connection.session(this._rootSessionId);
    if (!rootSession)
      throw new Error('Root session has been closed');
    await rootSession.send('Target.detachFromTarget', { sessionId: this._sessionId });
  }


_onClosed(browserDisconnectedLogs: string | undefined) {
    this._browserDisconnectedLogs = browserDisconnectedLogs;
    const errorMessage = browserDisconnectedLogs !== undefined ? 'Browser closed.' + browserDisconnectedLogs : 'Target closed';
    for (const callback of this._callbacks.values()) {
      callback.error.sessionClosed = true;
      callback.reject(rewriteErrorMessage(callback.error, errorMessage));
    }
    this._callbacks.clear();
    this._connection = null;
    Promise.resolve().then(() => this.emit(CRSessionEvents.Disconnected));
  }


export function format(content: string): string {
    try {
        const tokens = tokenize(content, { range: true });
        let result = content;

        // 在原来的基础上偏移量
        let offset = 0;

        tokens.forEach(item => {
            if (item.type === 'String' || item.type === 'Identifier') {
                let [start, end] = item.range;
                start += offset;
                end += offset;

                const value = item.value;

                const singleQReg = /^'(.+)'$/;
                const doubleQReg = /^"(.+)"$/;

                if (singleQReg.test(value)) {
                    result = `${result.slice(0, start)}"${value.slice(
                        1,
                        -1,
                    )}"${result.slice(end)}`;
                } else if (!singleQReg.test(value) && !doubleQReg.test(value)) {
                    result = `${result.slice(0, start)}"${value}"${result.slice(
                        end,
                    )}`;

                    offset += 2;
                }
            }
        });

        return result;
    } catch (error) {
        return content;
    }
}


item => {
            if (item.type === 'String' || item.type === 'Identifier') {
                let [start, end] = item.range;
                start += offset;
                end += offset;

                const value = item.value;

                const singleQReg = /^'(.+)'$/;
                const doubleQReg = /^"(.+)"$/;

                if (singleQReg.test(value)) {
                    result = `${result.slice(0, start)}"${value.slice(
                        1,
                        -1,
                    )}"${result.slice(end)}`;
                } else if (!singleQReg.test(value) && !doubleQReg.test(value)) {
                    result = `${result.slice(0, start)}"${value}"${result.slice(
                        end,
                    )}`;

                    offset += 2;
                }
            }
        }


function MarketRowItem({
  style,
  currency,
  counterCurrency,
  locale,
  loading,
  isStarred,
  toggleStar,
  selectCurrency,
  availableOnBuy,
  availableOnSwap,
}: Props) {
  const { t } = useTranslation();
  const dispatch = useDispatch();

  const openAddAccounts = useCallback(() => {
    if (currency)
      dispatch(
        openModal("MODAL_ADD_ACCOUNTS", {
          currency: currency.internalCurrency,
          preventSkippingCurrencySelection: true,
        }),
      );
  }, [dispatch, currency]);

  const history = useHistory();
  const { colors } = useTheme();
  const graphColor = colors.neutral.c80;
  const allAccounts = useSelector(accountsSelector);
  const flattenedAccounts = flattenAccounts(allAccounts);

  const onCurrencyClick = useCallback(() => {
    selectCurrency(currency.id);
    setTrackingSource("Page Market");
    history.push({
      pathname: `/market/${currency.id}`,
      state: currency,
    });
  }, [currency, history, selectCurrency]);

  const onBuy = useCallback(
    e => {
      e.preventDefault();
      e.stopPropagation();
      setTrackingSource("Page Market");
      history.push({
        pathname: "/exchange",
        state: {
          mode: "onRamp",
          defaultTicker: currency.ticker.toUpperCase(),
        },
      });
    },
    [currency, history],
  );

  const onSwap = useCallback(
    e => {
      if (currency?.internalCurrency?.id) {
        e.preventDefault();
        e.stopPropagation();
        setTrackingSource("Page Market");

        const currencyId = currency?.internalCurrency?.id;

        const defaultAccount = getAvailableAccountsById(currencyId, flattenedAccounts).find(
          Boolean,
        );

        if (!defaultAccount) return openAddAccounts();

        history.push({
          pathname: "/swap",
          state: {
            defaultCurrency: currency.internalCurrency,
            defaultAccount,
            defaultParentAccount: defaultAccount?.parentId
              ? flattenedAccounts.find(a => a.id === defaultAccount.parentId)
              : null,
          },
        });
      }
    },
    [currency?.internalCurrency, flattenedAccounts, openAddAccounts, history],
  );

  const onStarClick = useCallback(
    e => {
      e.preventDefault();
      e.stopPropagation();
      toggleStar();
    },
    [toggleStar],
  );

  const hasActions = currency?.internalCurrency && (availableOnBuy || availableOnSwap);

  return (
    <div style={{ ...style }}


() => {
    if (currency)
      dispatch(
        openModal("MODAL_ADD_ACCOUNTS", {
          currency: currency.internalCurrency,
          preventSkippingCurrencySelection: true,
        }),
      );
  }


() => {
    selectCurrency(currency.id);
    setTrackingSource("Page Market");
    history.push({
      pathname: `/market/${currency.id}`,
      state: currency,
    });
  }


e => {
      e.preventDefault();
      e.stopPropagation();
      setTrackingSource("Page Market");
      history.push({
        pathname: "/exchange",
        state: {
          mode: "onRamp",
          defaultTicker: currency.ticker.toUpperCase(),
        },
      });
    }


e => {
      if (currency?.internalCurrency?.id) {
        e.preventDefault();
        e.stopPropagation();
        setTrackingSource("Page Market");

        const currencyId = currency?.internalCurrency?.id;

        const defaultAccount = getAvailableAccountsById(currencyId, flattenedAccounts).find(
          Boolean,
        );

        if (!defaultAccount) return openAddAccounts();

        history.push({
          pathname: "/swap",
          state: {
            defaultCurrency: currency.internalCurrency,
            defaultAccount,
            defaultParentAccount: defaultAccount?.parentId
              ? flattenedAccounts.find(a => a.id === defaultAccount.parentId)
              : null,
          },
        });
      }
    }


a => a.id === defaultAccount.parentId


e => {
      e.preventDefault();
      e.stopPropagation();
      toggleStar();
    }


type Props = {
  currency: CurrencyData;
  counterCurrency: string;
  style: any;
  loading: boolean;
  locale: string;
  isStarred: boolean;
  toggleStar: () => void;
  selectCurrency: (currencyId: string) => void;
  availableOnBuy: boolean;
  availableOnSwap: boolean;
};


t("accounts.contextMenu.buy")


t("accounts.contextMenu.swap")


counterValueFormatter({ value: currency


parseFloat(currency


counterValueFormatter({
                shorten: true,
                currency: counterCurrency,
                value: currency


function getKeytip(keytips: IKeytipProps[], content: string): IKeytipProps | undefined {
    return find(keytips, (keytip: IKeytipProps) => {
      return keytip.content === content;
    });
  }


(keytip: IKeytipProps) => {
      return keytip.content === content;
    }


() => {
    if (ktpLayer) {
      ktpLayer.unmount();
    }
  }


() => {
    // Add some keytips to the Manager
    ktpMgr.keytips = {
      [uniqueIdB]: { keytip: keytipB, uniqueID: uniqueIdB },
      [uniqueIdG]: { keytip: keytipG, uniqueID: uniqueIdG },
    };

    // Create layer
    ktpLayer = mount(<KeytipLayerBase content="Alt Windows" />);

    const layerKeytips = ktpLayer.state('keytips');
    expect(layerKeytips).toHaveLength(2);
  }


() => {
    const onEnter = jest.fn();
    const onExit = jest.fn();

    beforeEach(() => {
      // Add keytips to the manager
      ktpMgr.keytips = {
        [uniqueIdB]: { keytip: keytipB, uniqueID: uniqueIdB },
        [uniqueIdC]: { keytip: keytipC, uniqueID: uniqueIdC },
        [uniqueIdD]: { keytip: keytipD, uniqueID: uniqueIdD },
      };
      ktpMgr.persistedKeytips = { [uniqueIdG]: { keytip: keytipG, uniqueID: uniqueIdG } };

      // Create layer
      ktpLayer = mount(
        <KeytipLayerBase
          componentRef={layerRef}
          content="Alt Windows"
          onEnterKeytipMode={onEnter}
          onExitKeytipMode={onExit}
        />,
      );
    });

    it('correctly sets variables when entering and exiting', () => {
      // Call enterKeytipMode
      ktpMgr.enterKeytipMode();
      expect(ktpLayer.state('inKeytipMode')).toEqual(true);
      expect(onEnter).toBeCalled();
      ktpTree = layerRef.current!.getKeytipTree();

      // 4 nodes + the root
      expect(Object.keys(ktpTree.nodeMap)).toHaveLength(5);
      expect(ktpTree.getNode(keytipIdB)).toBeDefined();
      expect(ktpTree.getNode(keytipIdC)).toBeDefined();
      expect(ktpTree.getNode(keytipIdD)).toBeDefined();
      expect(ktpTree.getNode(keytipIdG)).toBeDefined();

      // Should show B and C
      let visibleKeytips: IKeytipProps[] = ktpLayer.state('visibleKeytips');
      expect(visibleKeytips).toHaveLength(2);
      expect(getKeytip(visibleKeytips, 'B')).toBeDefined();
      expect(getKeytip(visibleKeytips, 'C')).toBeDefined();

      expect(ktpTree.currentKeytip).toEqual(ktpTree.root);

      // Call enterKeytipMode
      ktpMgr.exitKeytipMode();
      expect(ktpLayer.state('inKeytipMode')).toEqual(false);
      expect(onExit).toBeCalled();
      ktpTree = layerRef.current!.getKeytipTree();

      visibleKeytips = ktpLayer.state('visibleKeytips');
      expect(visibleKeytips).toHaveLength(0);

      expect(layerRef.current!.getCurrentSequence()).toEqual('');
      expect(ktpTree.currentKeytip).toBeUndefined();
    });

    it('respects shouldEnterKeytipMode', () => {
      ktpMgr.shouldEnterKeytipMode = false;
      ktpMgr.enterKeytipMode();
      expect(ktpLayer.state('inKeytipMode')).toEqual(false);
      ktpMgr.shouldEnterKeytipMode = true;
      ktpMgr.enterKeytipMode();
      expect(ktpLayer.state('inKeytipMode')).toEqual(true);
      expect(onEnter).toBeCalled();
    });
  }


() => {
      // Add keytips to the manager
      ktpMgr.keytips = {
        [uniqueIdB]: { keytip: keytipB, uniqueID: uniqueIdB },
        [uniqueIdC]: { keytip: keytipC, uniqueID: uniqueIdC },
        [uniqueIdD]: { keytip: keytipD, uniqueID: uniqueIdD },
      };
      ktpMgr.persistedKeytips = { [uniqueIdG]: { keytip: keytipG, uniqueID: uniqueIdG } };

      // Create layer
      ktpLayer = mount(
        <KeytipLayerBase
          componentRef={layerRef}
          content="Alt Windows"
          onEnterKeytipMode={onEnter}
          onExitKeytipMode={onExit}
        />,
      );
    }


() => {
      // Call enterKeytipMode
      ktpMgr.enterKeytipMode();
      expect(ktpLayer.state('inKeytipMode')).toEqual(true);
      expect(onEnter).toBeCalled();
      ktpTree = layerRef.current!.getKeytipTree();

      // 4 nodes + the root
      expect(Object.keys(ktpTree.nodeMap)).toHaveLength(5);
      expect(ktpTree.getNode(keytipIdB)).toBeDefined();
      expect(ktpTree.getNode(keytipIdC)).toBeDefined();
      expect(ktpTree.getNode(keytipIdD)).toBeDefined();
      expect(ktpTree.getNode(keytipIdG)).toBeDefined();

      // Should show B and C
      let visibleKeytips: IKeytipProps[] = ktpLayer.state('visibleKeytips');
      expect(visibleKeytips).toHaveLength(2);
      expect(getKeytip(visibleKeytips, 'B')).toBeDefined();
      expect(getKeytip(visibleKeytips, 'C')).toBeDefined();

      expect(ktpTree.currentKeytip).toEqual(ktpTree.root);

      // Call enterKeytipMode
      ktpMgr.exitKeytipMode();
      expect(ktpLayer.state('inKeytipMode')).toEqual(false);
      expect(onExit).toBeCalled();
      ktpTree = layerRef.current!.getKeytipTree();

      visibleKeytips = ktpLayer.state('visibleKeytips');
      expect(visibleKeytips).toHaveLength(0);

      expect(layerRef.current!.getCurrentSequence()).toEqual('');
      expect(ktpTree.currentKeytip).toBeUndefined();
    }


() => {
      ktpMgr.shouldEnterKeytipMode = false;
      ktpMgr.enterKeytipMode();
      expect(ktpLayer.state('inKeytipMode')).toEqual(false);
      ktpMgr.shouldEnterKeytipMode = true;
      ktpMgr.enterKeytipMode();
      expect(ktpLayer.state('inKeytipMode')).toEqual(true);
      expect(onEnter).toBeCalled();
    }


() => {
    let layerValue: KeytipLayerBase;

    const onEnter = jest.fn();
    const onExit = jest.fn();

    describe('processTransitionInput', () => {
      describe('with a default layer', () => {
        beforeEach(() => {
          // Add keytips to the manager
          ktpMgr.keytips = {
            [uniqueIdB]: { keytip: keytipB, uniqueID: uniqueIdB },
            [uniqueIdC]: { keytip: keytipC, uniqueID: uniqueIdC },
            [uniqueIdD]: { keytip: keytipD, uniqueID: uniqueIdD },
          };
          ktpMgr.persistedKeytips = { [uniqueIdG]: { keytip: keytipG, uniqueID: uniqueIdG } };

          // Create layer
          ktpLayer = mount(
            <KeytipLayerBase
              componentRef={layerRef}
              content="Alt Windows"
              onEnterKeytipMode={onEnter}
              onExitKeytipMode={onExit}
            />,
          );
          layerValue = layerRef.current!;
          ktpTree = layerValue.getKeytipTree();
        });

        it('calls on enter keytip mode when we process alt + left win', () => {
          layerValue.processTransitionInput({ key: 'Meta', modifierKeys: [KeyCodes.alt] });
          expect(onEnter).toBeCalledWith({ key: 'Meta', modifierKeys: [KeyCodes.alt] });
        });

        it('calls on exit keytip mode when we process alt + left win', () => {
          ktpTree.currentKeytip = ktpTree.root;
          layerValue.processTransitionInput({ key: 'Meta', modifierKeys: [KeyCodes.alt] });
          expect(onExit).toBeCalled();
        });

        it('calls on exit keytip mode because we are going back when on the root', () => {
          ktpTree.currentKeytip = ktpTree.root;
          layerValue.processTransitionInput({ key: 'Escape' });
          expect(onExit).toBeCalled();
        });

        it('hitting Esc when on a root child node will go up to the root', () => {
          const onReturn: jest.Mock = jest.fn();
          const nodeC = ktpTree.getNode(keytipIdC)!;
          nodeC.onReturn = onReturn;
          ktpTree.currentKeytip = nodeC;
          layerValue.processTransitionInput({ key: 'Escape' });
          expect(ktpTree.currentKeytip).toEqual(ktpTree.root);
          expect(onReturn).toBeCalled();
        });

        it('hitting Esc when on a child node will go up one level', () => {
          const onReturn: jest.Mock = jest.fn();
          const nodeD = ktpTree.getNode(keytipIdD)!;
          nodeD.onReturn = onReturn;
          ktpTree.currentKeytip = nodeD;
          layerValue.processTransitionInput({ key: 'Escape' });
          expect(ktpTree.currentKeytip).toEqual(ktpTree.getNode(keytipIdC));
        });
      });

      it('can handle using a single key that is also a modifier for transitions', () => {
        // Create layer
        ktpLayer = mount(
          <KeytipLayerBase
            content="Alt Windows"
            componentRef={layerRef}
            keytipStartSequences={[{ key: 'Meta' }]}
            onEnterKeytipMode={onEnter}
          />,
        );
        layerValue = layerRef.current!;
        layerValue.processTransitionInput({ key: 'Meta' });
        expect(onEnter).toBeCalledWith({ key: 'Meta' });
      });
    });

    describe('processInput', () => {
      beforeEach(() => {
        // Add keytips to the manager
        ktpMgr.keytips = {
          [uniqueIdB]: { keytip: keytipB, uniqueID: uniqueIdB },
          [uniqueIdC]: { keytip: keytipC, uniqueID: uniqueIdC },
          [uniqueIdD]: { keytip: keytipD, uniqueID: uniqueIdD },
          [uniqueIdE1]: { keytip: keytipE1, uniqueID: uniqueIdE1 },
          [uniqueIdE2]: { keytip: keytipE2, uniqueID: uniqueIdE2 },
        };
        ktpMgr.persistedKeytips = { [uniqueIdG]: { keytip: keytipG, uniqueID: uniqueIdG } };

        // Create layer
        ktpLayer = mount(
          <KeytipLayerBase
            componentRef={layerRef}
            content="Alt Windows"
            onEnterKeytipMode={onEnter}
            onExitKeytipMode={onExit}
          />,
        );
        layerValue = layerRef.current!;
        ktpTree = layerValue.getKeytipTree();
      });

      // Processing keys tests
      it('Processing a leaf node should execute it`s onExecute func and trigger onExitKeytipMode', () => {
        const onExecute: jest.Mock = jest.fn();
        ktpTree.addNode({ ...keytipB, onExecute }, uniqueIdB);
        ktpTree.currentKeytip = ktpTree.root;
        layerValue.processInput('b');
        expect(onExecute).toBeCalled();
        expect(onExit).toBeCalled();
        expect(layerValue.getCurrentSequence().length).toEqual(0);
      });

      it('Processing a node with two keys should save sequence and wait for second key', () => {
        const onExecuteE2: jest.Mock = jest.fn();
        ktpTree.addNode({ ...keytipE2, onExecute: onExecuteE2 }, uniqueIdE2);
        ktpTree.currentKeytip = ktpTree.root;
        layerValue.processInput('e');
        // We are still waiting for second key
        expect(layerValue.getCurrentSequence().length).toEqual(1);
        layerValue.processInput('2');
        expect(onExecuteE2).toBeCalled();
        expect(layerValue.getCurrentSequence().length).toEqual(0);
        expect(onExit).toBeCalled();
      });

      it('Processing a node which is not leaf but its children are not in the DOM', () => {
        const onExecute: jest.Mock = jest.fn();
        ktpTree.addNode({ ...keytipB, onExecute, hasDynamicChildren: true }, uniqueIdB);
        ktpTree.currentKeytip = ktpTree.root;
        layerValue.processInput('b');
        // Node B's onExecute should be called
        expect(onExecute).toBeCalled();
        // There is no more buffer in the sequence
        expect(layerValue.getCurrentSequence().length).toEqual(0);
        // We haven't exited keytip mode (current keytip is set to the matched keytip)
        expect(ktpTree.currentKeytip.id).toEqual(keytipIdB);
      });

      it('with a persisted node will partially match the keytip but won`t show it', () => {
        // Make E2 a persisted node
        const nodeE2 = ktpTree.getNode(keytipIdE2)!;
        nodeE2.persisted = true;
        nodeE2.onExecute = jest.fn();
        ktpTree.currentKeytip = ktpTree.root;
        layerValue.processInput('e');
        // Only E1 should be visible
        const visibleLayerKtps: IKeytipProps[] = ktpLayer.state('visibleKeytips');
        expect(visibleLayerKtps).toHaveLength(1);
        expect(visibleLayerKtps[0].content).toEqual('E1');
        layerValue.processInput('2');
        // E2 should be triggered
        expect(nodeE2.onExecute).toBeCalled();
      });
    });
  }


() => {
      describe('with a default layer', () => {
        beforeEach(() => {
          // Add keytips to the manager
          ktpMgr.keytips = {
            [uniqueIdB]: { keytip: keytipB, uniqueID: uniqueIdB },
            [uniqueIdC]: { keytip: keytipC, uniqueID: uniqueIdC },
            [uniqueIdD]: { keytip: keytipD, uniqueID: uniqueIdD },
          };
          ktpMgr.persistedKeytips = { [uniqueIdG]: { keytip: keytipG, uniqueID: uniqueIdG } };

          // Create layer
          ktpLayer = mount(
            <KeytipLayerBase
              componentRef={layerRef}
              content="Alt Windows"
              onEnterKeytipMode={onEnter}
              onExitKeytipMode={onExit}
            />,
          );
          layerValue = layerRef.current!;
          ktpTree = layerValue.getKeytipTree();
        });

        it('calls on enter keytip mode when we process alt + left win', () => {
          layerValue.processTransitionInput({ key: 'Meta', modifierKeys: [KeyCodes.alt] });
          expect(onEnter).toBeCalledWith({ key: 'Meta', modifierKeys: [KeyCodes.alt] });
        });

        it('calls on exit keytip mode when we process alt + left win', () => {
          ktpTree.currentKeytip = ktpTree.root;
          layerValue.processTransitionInput({ key: 'Meta', modifierKeys: [KeyCodes.alt] });
          expect(onExit).toBeCalled();
        });

        it('calls on exit keytip mode because we are going back when on the root', () => {
          ktpTree.currentKeytip = ktpTree.root;
          layerValue.processTransitionInput({ key: 'Escape' });
          expect(onExit).toBeCalled();
        });

        it('hitting Esc when on a root child node will go up to the root', () => {
          const onReturn: jest.Mock = jest.fn();
          const nodeC = ktpTree.getNode(keytipIdC)!;
          nodeC.onReturn = onReturn;
          ktpTree.currentKeytip = nodeC;
          layerValue.processTransitionInput({ key: 'Escape' });
          expect(ktpTree.currentKeytip).toEqual(ktpTree.root);
          expect(onReturn).toBeCalled();
        });

        it('hitting Esc when on a child node will go up one level', () => {
          const onReturn: jest.Mock = jest.fn();
          const nodeD = ktpTree.getNode(keytipIdD)!;
          nodeD.onReturn = onReturn;
          ktpTree.currentKeytip = nodeD;
          layerValue.processTransitionInput({ key: 'Escape' });
          expect(ktpTree.currentKeytip).toEqual(ktpTree.getNode(keytipIdC));
        });
      });

      it('can handle using a single key that is also a modifier for transitions', () => {
        // Create layer
        ktpLayer = mount(
          <KeytipLayerBase
            content="Alt Windows"
            componentRef={layerRef}
            keytipStartSequences={[{ key: 'Meta' }]}
            onEnterKeytipMode={onEnter}
          />,
        );
        layerValue = layerRef.current!;
        layerValue.processTransitionInput({ key: 'Meta' });
        expect(onEnter).toBeCalledWith({ key: 'Meta' });
      });
    }


() => {
        beforeEach(() => {
          // Add keytips to the manager
          ktpMgr.keytips = {
            [uniqueIdB]: { keytip: keytipB, uniqueID: uniqueIdB },
            [uniqueIdC]: { keytip: keytipC, uniqueID: uniqueIdC },
            [uniqueIdD]: { keytip: keytipD, uniqueID: uniqueIdD },
          };
          ktpMgr.persistedKeytips = { [uniqueIdG]: { keytip: keytipG, uniqueID: uniqueIdG } };

          // Create layer
          ktpLayer = mount(
            <KeytipLayerBase
              componentRef={layerRef}
              content="Alt Windows"
              onEnterKeytipMode={onEnter}
              onExitKeytipMode={onExit}
            />,
          );
          layerValue = layerRef.current!;
          ktpTree = layerValue.getKeytipTree();
        });

        it('calls on enter keytip mode when we process alt + left win', () => {
          layerValue.processTransitionInput({ key: 'Meta', modifierKeys: [KeyCodes.alt] });
          expect(onEnter).toBeCalledWith({ key: 'Meta', modifierKeys: [KeyCodes.alt] });
        });

        it('calls on exit keytip mode when we process alt + left win', () => {
          ktpTree.currentKeytip = ktpTree.root;
          layerValue.processTransitionInput({ key: 'Meta', modifierKeys: [KeyCodes.alt] });
          expect(onExit).toBeCalled();
        });

        it('calls on exit keytip mode because we are going back when on the root', () => {
          ktpTree.currentKeytip = ktpTree.root;
          layerValue.processTransitionInput({ key: 'Escape' });
          expect(onExit).toBeCalled();
        });

        it('hitting Esc when on a root child node will go up to the root', () => {
          const onReturn: jest.Mock = jest.fn();
          const nodeC = ktpTree.getNode(keytipIdC)!;
          nodeC.onReturn = onReturn;
          ktpTree.currentKeytip = nodeC;
          layerValue.processTransitionInput({ key: 'Escape' });
          expect(ktpTree.currentKeytip).toEqual(ktpTree.root);
          expect(onReturn).toBeCalled();
        });

        it('hitting Esc when on a child node will go up one level', () => {
          const onReturn: jest.Mock = jest.fn();
          const nodeD = ktpTree.getNode(keytipIdD)!;
          nodeD.onReturn = onReturn;
          ktpTree.currentKeytip = nodeD;
          layerValue.processTransitionInput({ key: 'Escape' });
          expect(ktpTree.currentKeytip).toEqual(ktpTree.getNode(keytipIdC));
        });
      }


() => {
          // Add keytips to the manager
          ktpMgr.keytips = {
            [uniqueIdB]: { keytip: keytipB, uniqueID: uniqueIdB },
            [uniqueIdC]: { keytip: keytipC, uniqueID: uniqueIdC },
            [uniqueIdD]: { keytip: keytipD, uniqueID: uniqueIdD },
          };
          ktpMgr.persistedKeytips = { [uniqueIdG]: { keytip: keytipG, uniqueID: uniqueIdG } };

          // Create layer
          ktpLayer = mount(
            <KeytipLayerBase
              componentRef={layerRef}
              content="Alt Windows"
              onEnterKeytipMode={onEnter}
              onExitKeytipMode={onExit}
            />,
          );
          layerValue = layerRef.current!;
          ktpTree = layerValue.getKeytipTree();
        }


() => {
          layerValue.processTransitionInput({ key: 'Meta', modifierKeys: [KeyCodes.alt] });
          expect(onEnter).toBeCalledWith({ key: 'Meta', modifierKeys: [KeyCodes.alt] });
        }


() => {
          ktpTree.currentKeytip = ktpTree.root;
          layerValue.processTransitionInput({ key: 'Meta', modifierKeys: [KeyCodes.alt] });
          expect(onExit).toBeCalled();
        }


() => {
          ktpTree.currentKeytip = ktpTree.root;
          layerValue.processTransitionInput({ key: 'Escape' });
          expect(onExit).toBeCalled();
        }


() => {
          const onReturn: jest.Mock = jest.fn();
          const nodeC = ktpTree.getNode(keytipIdC)!;
          nodeC.onReturn = onReturn;
          ktpTree.currentKeytip = nodeC;
          layerValue.processTransitionInput({ key: 'Escape' });
          expect(ktpTree.currentKeytip).toEqual(ktpTree.root);
          expect(onReturn).toBeCalled();
        }


() => {
          const onReturn: jest.Mock = jest.fn();
          const nodeD = ktpTree.getNode(keytipIdD)!;
          nodeD.onReturn = onReturn;
          ktpTree.currentKeytip = nodeD;
          layerValue.processTransitionInput({ key: 'Escape' });
          expect(ktpTree.currentKeytip).toEqual(ktpTree.getNode(keytipIdC));
        }


() => {
        // Create layer
        ktpLayer = mount(
          <KeytipLayerBase
            content="Alt Windows"
            componentRef={layerRef}
            keytipStartSequences={[{ key: 'Meta' }]}
            onEnterKeytipMode={onEnter}
          />,
        );
        layerValue = layerRef.current!;
        layerValue.processTransitionInput({ key: 'Meta' });
        expect(onEnter).toBeCalledWith({ key: 'Meta' });
      }


() => {
      beforeEach(() => {
        // Add keytips to the manager
        ktpMgr.keytips = {
          [uniqueIdB]: { keytip: keytipB, uniqueID: uniqueIdB },
          [uniqueIdC]: { keytip: keytipC, uniqueID: uniqueIdC },
          [uniqueIdD]: { keytip: keytipD, uniqueID: uniqueIdD },
          [uniqueIdE1]: { keytip: keytipE1, uniqueID: uniqueIdE1 },
          [uniqueIdE2]: { keytip: keytipE2, uniqueID: uniqueIdE2 },
        };
        ktpMgr.persistedKeytips = { [uniqueIdG]: { keytip: keytipG, uniqueID: uniqueIdG } };

        // Create layer
        ktpLayer = mount(
          <KeytipLayerBase
            componentRef={layerRef}
            content="Alt Windows"
            onEnterKeytipMode={onEnter}
            onExitKeytipMode={onExit}
          />,
        );
        layerValue = layerRef.current!;
        ktpTree = layerValue.getKeytipTree();
      });

      // Processing keys tests
      it('Processing a leaf node should execute it`s onExecute func and trigger onExitKeytipMode', () => {
        const onExecute: jest.Mock = jest.fn();
        ktpTree.addNode({ ...keytipB, onExecute }, uniqueIdB);
        ktpTree.currentKeytip = ktpTree.root;
        layerValue.processInput('b');
        expect(onExecute).toBeCalled();
        expect(onExit).toBeCalled();
        expect(layerValue.getCurrentSequence().length).toEqual(0);
      });

      it('Processing a node with two keys should save sequence and wait for second key', () => {
        const onExecuteE2: jest.Mock = jest.fn();
        ktpTree.addNode({ ...keytipE2, onExecute: onExecuteE2 }, uniqueIdE2);
        ktpTree.currentKeytip = ktpTree.root;
        layerValue.processInput('e');
        // We are still waiting for second key
        expect(layerValue.getCurrentSequence().length).toEqual(1);
        layerValue.processInput('2');
        expect(onExecuteE2).toBeCalled();
        expect(layerValue.getCurrentSequence().length).toEqual(0);
        expect(onExit).toBeCalled();
      });

      it('Processing a node which is not leaf but its children are not in the DOM', () => {
        const onExecute: jest.Mock = jest.fn();
        ktpTree.addNode({ ...keytipB, onExecute, hasDynamicChildren: true }, uniqueIdB);
        ktpTree.currentKeytip = ktpTree.root;
        layerValue.processInput('b');
        // Node B's onExecute should be called
        expect(onExecute).toBeCalled();
        // There is no more buffer in the sequence
        expect(layerValue.getCurrentSequence().length).toEqual(0);
        // We haven't exited keytip mode (current keytip is set to the matched keytip)
        expect(ktpTree.currentKeytip.id).toEqual(keytipIdB);
      });

      it('with a persisted node will partially match the keytip but won`t show it', () => {
        // Make E2 a persisted node
        const nodeE2 = ktpTree.getNode(keytipIdE2)!;
        nodeE2.persisted = true;
        nodeE2.onExecute = jest.fn();
        ktpTree.currentKeytip = ktpTree.root;
        layerValue.processInput('e');
        // Only E1 should be visible
        const visibleLayerKtps: IKeytipProps[] = ktpLayer.state('visibleKeytips');
        expect(visibleLayerKtps).toHaveLength(1);
        expect(visibleLayerKtps[0].content).toEqual('E1');
        layerValue.processInput('2');
        // E2 should be triggered
        expect(nodeE2.onExecute).toBeCalled();
      });
    }


() => {
        // Add keytips to the manager
        ktpMgr.keytips = {
          [uniqueIdB]: { keytip: keytipB, uniqueID: uniqueIdB },
          [uniqueIdC]: { keytip: keytipC, uniqueID: uniqueIdC },
          [uniqueIdD]: { keytip: keytipD, uniqueID: uniqueIdD },
          [uniqueIdE1]: { keytip: keytipE1, uniqueID: uniqueIdE1 },
          [uniqueIdE2]: { keytip: keytipE2, uniqueID: uniqueIdE2 },
        };
        ktpMgr.persistedKeytips = { [uniqueIdG]: { keytip: keytipG, uniqueID: uniqueIdG } };

        // Create layer
        ktpLayer = mount(
          <KeytipLayerBase
            componentRef={layerRef}
            content="Alt Windows"
            onEnterKeytipMode={onEnter}
            onExitKeytipMode={onExit}
          />,
        );
        layerValue = layerRef.current!;
        ktpTree = layerValue.getKeytipTree();
      }


() => {
        const onExecute: jest.Mock = jest.fn();
        ktpTree.addNode({ ...keytipB, onExecute }, uniqueIdB);
        ktpTree.currentKeytip = ktpTree.root;
        layerValue.processInput('b');
        expect(onExecute).toBeCalled();
        expect(onExit).toBeCalled();
        expect(layerValue.getCurrentSequence().length).toEqual(0);
      }


() => {
        const onExecuteE2: jest.Mock = jest.fn();
        ktpTree.addNode({ ...keytipE2, onExecute: onExecuteE2 }, uniqueIdE2);
        ktpTree.currentKeytip = ktpTree.root;
        layerValue.processInput('e');
        // We are still waiting for second key
        expect(layerValue.getCurrentSequence().length).toEqual(1);
        layerValue.processInput('2');
        expect(onExecuteE2).toBeCalled();
        expect(layerValue.getCurrentSequence().length).toEqual(0);
        expect(onExit).toBeCalled();
      }


() => {
        const onExecute: jest.Mock = jest.fn();
        ktpTree.addNode({ ...keytipB, onExecute, hasDynamicChildren: true }, uniqueIdB);
        ktpTree.currentKeytip = ktpTree.root;
        layerValue.processInput('b');
        // Node B's onExecute should be called
        expect(onExecute).toBeCalled();
        // There is no more buffer in the sequence
        expect(layerValue.getCurrentSequence().length).toEqual(0);
        // We haven't exited keytip mode (current keytip is set to the matched keytip)
        expect(ktpTree.currentKeytip.id).toEqual(keytipIdB);
      }


() => {
        // Make E2 a persisted node
        const nodeE2 = ktpTree.getNode(keytipIdE2)!;
        nodeE2.persisted = true;
        nodeE2.onExecute = jest.fn();
        ktpTree.currentKeytip = ktpTree.root;
        layerValue.processInput('e');
        // Only E1 should be visible
        const visibleLayerKtps: IKeytipProps[] = ktpLayer.state('visibleKeytips');
        expect(visibleLayerKtps).toHaveLength(1);
        expect(visibleLayerKtps[0].content).toEqual('E1');
        layerValue.processInput('2');
        // E2 should be triggered
        expect(nodeE2.onExecute).toBeCalled();
      }


() => {
    // Create layer
    it('shows the defined keytips and hides all others', () => {
      ktpMgr.keytips = {
        [uniqueIdB]: { keytip: keytipB, uniqueID: uniqueIdB },
        [uniqueIdC]: { keytip: keytipC, uniqueID: uniqueIdC },
        [uniqueIdD]: { keytip: keytipD, uniqueID: uniqueIdD },
        [uniqueIdE1]: { keytip: keytipE1, uniqueID: uniqueIdE1 },
        [uniqueIdE2]: { keytip: keytipE2, uniqueID: uniqueIdE2 },
      };
      ktpLayer = mount(<KeytipLayerBase componentRef={layerRef} content="Alt Windows" />);
      layerRef.current!.showKeytips([keytipIdB, keytipIdC]);
      const visibleKeytips: IKeytipProps[] = ktpLayer.state('visibleKeytips');
      expect(visibleKeytips).toHaveLength(2);
      expect(getKeytip(visibleKeytips, 'B')).toBeDefined();
      expect(getKeytip(visibleKeytips, 'C')).toBeDefined();
    });

    it('should handle overflowSetSequence correctly', () => {
      ktpMgr.keytips = {
        [uniqueIdB]: {
          keytip: {
            ...keytipB,
            overflowSetSequence: ['x'],
          },
          uniqueID: uniqueIdB,
        },
        [uniqueIdC]: { keytip: keytipC, uniqueID: uniqueIdC },
        [uniqueIdD]: { keytip: keytipD, uniqueID: uniqueIdD },
        [uniqueIdE1]: { keytip: keytipE1, uniqueID: uniqueIdE1 },
        [uniqueIdE2]: { keytip: keytipE2, uniqueID: uniqueIdE2 },
      };
      ktpLayer = mount(<KeytipLayerBase componentRef={layerRef} content="Alt Windows" />);
      layerRef.current!.showKeytips(['ktp-x-b']);
      const visibleKeytips: IKeytipProps[] = ktpLayer.state('visibleKeytips');
      expect(visibleKeytips).toHaveLength(1);
      expect(getKeytip(visibleKeytips, 'B')).toBeDefined();
    });
  }


() => {
      ktpMgr.keytips = {
        [uniqueIdB]: { keytip: keytipB, uniqueID: uniqueIdB },
        [uniqueIdC]: { keytip: keytipC, uniqueID: uniqueIdC },
        [uniqueIdD]: { keytip: keytipD, uniqueID: uniqueIdD },
        [uniqueIdE1]: { keytip: keytipE1, uniqueID: uniqueIdE1 },
        [uniqueIdE2]: { keytip: keytipE2, uniqueID: uniqueIdE2 },
      };
      ktpLayer = mount(<KeytipLayerBase componentRef={layerRef} content="Alt Windows" />);
      layerRef.current!.showKeytips([keytipIdB, keytipIdC]);
      const visibleKeytips: IKeytipProps[] = ktpLayer.state('visibleKeytips');
      expect(visibleKeytips).toHaveLength(2);
      expect(getKeytip(visibleKeytips, 'B')).toBeDefined();
      expect(getKeytip(visibleKeytips, 'C')).toBeDefined();
    }


() => {
      ktpMgr.keytips = {
        [uniqueIdB]: {
          keytip: {
            ...keytipB,
            overflowSetSequence: ['x'],
          },
          uniqueID: uniqueIdB,
        },
        [uniqueIdC]: { keytip: keytipC, uniqueID: uniqueIdC },
        [uniqueIdD]: { keytip: keytipD, uniqueID: uniqueIdD },
        [uniqueIdE1]: { keytip: keytipE1, uniqueID: uniqueIdE1 },
        [uniqueIdE2]: { keytip: keytipE2, uniqueID: uniqueIdE2 },
      };
      ktpLayer = mount(<KeytipLayerBase componentRef={layerRef} content="Alt Windows" />);
      layerRef.current!.showKeytips(['ktp-x-b']);
      const visibleKeytips: IKeytipProps[] = ktpLayer.state('visibleKeytips');
      expect(visibleKeytips).toHaveLength(1);
      expect(getKeytip(visibleKeytips, 'B')).toBeDefined();
    }


() => {
    beforeEach(() => {
      jest.useFakeTimers();
      ktpMgr.delayUpdatingKeytipChange = false;
      ktpMgr.inKeytipMode = false;

      // Add keytips to the manager
      ktpMgr.keytips = {
        [uniqueIdB]: { keytip: keytipB, uniqueID: uniqueIdB },
        [uniqueIdC]: { keytip: keytipC, uniqueID: uniqueIdC },
        [uniqueIdD]: { keytip: keytipD, uniqueID: uniqueIdD },
        [uniqueIdE1]: { keytip: keytipE1, uniqueID: uniqueIdE1 },
        [uniqueIdE2]: { keytip: keytipE2, uniqueID: uniqueIdE2 },
      };
      ktpMgr.persistedKeytips = { [uniqueIdG]: { keytip: keytipG, uniqueID: uniqueIdG } };

      // Create layer
      ktpLayer = mount(<KeytipLayerBase componentRef={layerRef} content="Alt Windows" />);
      ktpTree = layerRef.current!.getKeytipTree();
    });

    afterEach(() => {
      jest.useRealTimers();
    });

    it('keytipAdded event delay-shows a keytip if the current keytip is its parent', () => {
      ktpTree.currentKeytip = ktpTree.getNode(keytipIdB);
      // Add a child under B
      ktpMgr.register({
        content: 'X',
        keySequences: ['b', 'x'],
      });
      jest.runAllTimers();

      const visibleKeytips: IKeytipProps[] = ktpLayer.state('visibleKeytips');
      expect(visibleKeytips).toHaveLength(1);
      expect(getKeytip(visibleKeytips, 'X')).toBeDefined();
    });

    // eslint-disable-next-line @fluentui/max-len
    it('keytipAdded event does not show a keytip if the current keytip is its parent when delay updating and not in keytip mode', () => {
      ktpMgr.delayUpdatingKeytipChange = true;
      ktpTree.currentKeytip = ktpTree.getNode(keytipIdB);
      // Add a child under B
      ktpMgr.register({
        content: 'X',
        keySequences: ['b', 'x'],
      });
      jest.runAllTimers();

      const visibleKeytips: IKeytipProps[] = ktpLayer.state('visibleKeytips');
      expect(visibleKeytips).toHaveLength(0);
      expect(getKeytip(visibleKeytips, 'X')).toBeUndefined();
    });

    // eslint-disable-next-line @fluentui/max-len
    it('keytipAdded event delay-shows a keytip if the current keytip is its parent when delay updating and in keytip mode', () => {
      ktpMgr.delayUpdatingKeytipChange = true;
      ktpMgr.inKeytipMode = true;
      ktpTree.currentKeytip = ktpTree.getNode(keytipIdB);
      // Add a child under B
      ktpMgr.register({
        content: 'X',
        keySequences: ['b', 'x'],
      });
      jest.runAllTimers();

      const visibleKeytips: IKeytipProps[] = ktpLayer.state('visibleKeytips');
      expect(visibleKeytips).toHaveLength(1);
      expect(getKeytip(visibleKeytips, 'X')).toBeDefined();
    });
  }


() => {
      jest.useFakeTimers();
      ktpMgr.delayUpdatingKeytipChange = false;
      ktpMgr.inKeytipMode = false;

      // Add keytips to the manager
      ktpMgr.keytips = {
        [uniqueIdB]: { keytip: keytipB, uniqueID: uniqueIdB },
        [uniqueIdC]: { keytip: keytipC, uniqueID: uniqueIdC },
        [uniqueIdD]: { keytip: keytipD, uniqueID: uniqueIdD },
        [uniqueIdE1]: { keytip: keytipE1, uniqueID: uniqueIdE1 },
        [uniqueIdE2]: { keytip: keytipE2, uniqueID: uniqueIdE2 },
      };
      ktpMgr.persistedKeytips = { [uniqueIdG]: { keytip: keytipG, uniqueID: uniqueIdG } };

      // Create layer
      ktpLayer = mount(<KeytipLayerBase componentRef={layerRef} content="Alt Windows" />);
      ktpTree = layerRef.current!.getKeytipTree();
    }


() => {
      jest.useRealTimers();
    }


() => {
      ktpTree.currentKeytip = ktpTree.getNode(keytipIdB);
      // Add a child under B
      ktpMgr.register({
        content: 'X',
        keySequences: ['b', 'x'],
      });
      jest.runAllTimers();

      const visibleKeytips: IKeytipProps[] = ktpLayer.state('visibleKeytips');
      expect(visibleKeytips).toHaveLength(1);
      expect(getKeytip(visibleKeytips, 'X')).toBeDefined();
    }


() => {
      ktpMgr.delayUpdatingKeytipChange = true;
      ktpTree.currentKeytip = ktpTree.getNode(keytipIdB);
      // Add a child under B
      ktpMgr.register({
        content: 'X',
        keySequences: ['b', 'x'],
      });
      jest.runAllTimers();

      const visibleKeytips: IKeytipProps[] = ktpLayer.state('visibleKeytips');
      expect(visibleKeytips).toHaveLength(0);
      expect(getKeytip(visibleKeytips, 'X')).toBeUndefined();
    }


() => {
      ktpMgr.delayUpdatingKeytipChange = true;
      ktpMgr.inKeytipMode = true;
      ktpTree.currentKeytip = ktpTree.getNode(keytipIdB);
      // Add a child under B
      ktpMgr.register({
        content: 'X',
        keySequences: ['b', 'x'],
      });
      jest.runAllTimers();

      const visibleKeytips: IKeytipProps[] = ktpLayer.state('visibleKeytips');
      expect(visibleKeytips).toHaveLength(1);
      expect(getKeytip(visibleKeytips, 'X')).toBeDefined();
    }


() => {
  const [visible, setVisible] = useState(false);

  return (
    <>
      <Tool>
        <Button type="primary" onClick={()


/**
 * A component that formats all the data associated with a batch for display.
 */
@Component({
  selector: 'batch',
  templateUrl: './batch.component.html',
  styleUrls: [
    './batch.component.scss',
    '../../../styles/shared/_explorer-detail.scss'
  ],
  providers: [Base64DecodePipe]
})
export class BatchComponent implements OnInit {

  // data representing the batch
  @Input() data: any = {};

  // whether transaction data on this batch is shown as JSON
  @Input() showDataAsJSON?: boolean;

  // data stringified for Angular UI Ace to display
  jsonData = '{}';

  aceMode = 'json';

  /**
   * @param base64DecodePipe {Base64DecodePipe} -- used for decoding base64
   *   to ascii strings
   */
  constructor(private base64DecodePipe: Base64DecodePipe) {}

  ngOnInit(): void {
    // format data for Angular UI Ace
    this.jsonData = this.formatJSONData(this.data);
  }

  /**
   * Formats batch data into a JSON string with proper indentation for display.
   * @param transactionData - data representing a transaction within the batch
   * @returns {string} formatted JSON string of the data
   */
  formatJSONData(data: any) {
    if (!data) data = {};

    // format transactions within the batch data
    if (data.transactions && this.showDataAsJSON) {
      for (var txnIdx in data.transactions) {
        data.transactions[txnIdx] =
          this.formatTransactionData(data.transactions[txnIdx]);
      }
    }

    return JSON.stringify(data, null, 2);
  }

  /**
   * Formats information held in the payloads in a batch's transactions.
   * @param transactionData - data representing a transaction within the batch
   * @returns {object} formatted transaction data
   */
  formatTransactionData(transactionData: any): string {
    // decode transaction's payload from base64 into JSON
    if (transactionData && transactionData.payload) {
      transactionData.payload =
        this.base64DecodePipe.transform(transactionData.payload);
        try {
          transactionData.payload = JSON.parse(transactionData.payload);
        } catch(e) {}
    }
    return transactionData;
  }
}


ngOnInit(): void {
    // format data for Angular UI Ace
    this.jsonData = this.formatJSONData(this.data);
  }


/**
   * Formats batch data into a JSON string with proper indentation for display.
   * @param transactionData - data representing a transaction within the batch
   * @returns {string} formatted JSON string of the data
   */
formatJSONData(data: any) {
    if (!data) data = {};

    // format transactions within the batch data
    if (data.transactions && this.showDataAsJSON) {
      for (var txnIdx in data.transactions) {
        data.transactions[txnIdx] =
          this.formatTransactionData(data.transactions[txnIdx]);
      }
    }

    return JSON.stringify(data, null, 2);
  }


/**
   * Formats information held in the payloads in a batch's transactions.
   * @param transactionData - data representing a transaction within the batch
   * @returns {object} formatted transaction data
   */
formatTransactionData(transactionData: any): string {
    // decode transaction's payload from base64 into JSON
    if (transactionData && transactionData.payload) {
      transactionData.payload =
        this.base64DecodePipe.transform(transactionData.payload);
        try {
          transactionData.payload = JSON.parse(transactionData.payload);
        } catch(e) {}
    }
    return transactionData;
  }


message => JSON.parse(message.payload.toString()) as Observation


observation => console.log(`Observation: ${observation.result}`)


@Injectable({
  providedIn: 'root'
})
export class StaMqttInterfaceService {

  private mqttService: MqttService;

  constructor() {
    this.mqttService = new MqttService({
      hostname: AppConfig.settings.sta.mqtt.hostname,
      port: AppConfig.settings.sta.mqtt.port,
      path: AppConfig.settings.sta.mqtt.path
    });
  }

  public subscribeDatastreamObservations(
    datasetId: string,
    params?: StaFilter<ObservationSelectParams, ObservationExpandParams>
  ): Observable<Observation> {
    // TODO: add filterung: $select=result,phenomenonTime
    return this.mqttService.observe(`Datastreams(${datasetId})/Observations`).pipe(
      map(message => JSON.parse(message.payload.toString()) as Observation),
      // tap(observation => console.log(`Observation for ${datasetId} with result: ${JSON.stringify(observation, null, 2)}`))
    );
  }

  public subscribeObservations(): Observable<Observation> {
    return this.mqttService.observe(`Observations`).pipe(
      map(message => JSON.parse(message.payload.toString()) as Observation),
      tap(observation => console.log(`Observation: ${observation.result}`))
    );
  }

}


public subscribeDatastreamObservations(
    datasetId: string,
    params?: StaFilter<ObservationSelectParams, ObservationExpandParams>
  ): Observable<Observation> {
    // TODO: add filterung: $select=result,phenomenonTime
    return this.mqttService.observe(`Datastreams(${datasetId})/Observations`).pipe(
      map(message => JSON.parse(message.payload.toString()) as Observation),
      // tap(observation => console.log(`Observation for ${datasetId} with result: ${JSON.stringify(observation, null, 2)}`))
    );
  }


public subscribeObservations(): Observable<Observation> {
    return this.mqttService.observe(`Observations`).pipe(
      map(message => JSON.parse(message.payload.toString()) as Observation),
      tap(observation => console.log(`Observation: ${observation.result}`))
    );
  }


export default class ChatController extends Controller {
  async index() {
    const { ctx } = this;
    try {
      const socket: any = ctx.socket;
      const message = ctx.args[0] || {};
      const { roomId, receive } = message;
      socket.broadcast.emit(roomId, {
        ...message,
        target: roomId
      });
      socket.broadcast.emit(receive, {
        ...message,
        target: receive
      });
    } catch (error) {
      this.app.logger.error(error);
    }
  }
}


async index() {
    const { ctx } = this;
    try {
      const socket: any = ctx.socket;
      const message = ctx.args[0] || {};
      const { roomId, receive } = message;
      socket.broadcast.emit(roomId, {
        ...message,
        target: roomId
      });
      socket.broadcast.emit(receive, {
        ...message,
        target: receive
      });
    } catch (error) {
      this.app.logger.error(error);
    }
  }


function App() {
  return (
    <BrowserRouter>
      <AuthContextProvider>
        <Switch>
          <Route path="/" exact component={Home} />
          <Route path="/rooms/new" component={NewRoom} />
          <Route path="/rooms/:id" component={Room} />

          <Route path="/admin/rooms/:id" component={AdminRoom} />
        </Switch>
      </AuthContextProvider>
    </BrowserRouter>
  );
}


@Injectable()
export class VendorBillService {

  options: any;
  constructor(private http: Http) {
    let token = localStorage.getItem('userToken');
    let headers = new Headers({ 'Content-Type': 'application/json' });
    headers.append('Authorization', token)
    this.options = new RequestOptions({ headers: headers });

  }

  getVendorBillProducts(url: string, input: any): any {
    return this.http.post(url, input, this.options)
      .map((response: Response) => response.json());
  }

  saveVendorBillPayment(url: string, input: any): any {
    return this.http.post(url, input, this.options)
      .map((response: Response) => response.json());
  }

  deleteVendorBillPayment(url: string, input: any): any {
    return this.http.post(url, input, this.options)
      .map((response: Response) => response.json());
  }

  saveVendorBill(url: string, input: any): any {
    return this.http.post(url, input, this.options)
      .map((response: Response) => response.json());
  }

  getAllVendorBills(url: string, input: any): any {
    return this.http.post(url, input, this.options)
      .map((response: Response) => response.json());
  }

  getVendorBillPayments(url: string, input: any): any {
    return this.http.post(url, input, this.options)
      .map((response: Response) => response.json());
  }

  deleteVendorBill(url: string, input: any): any {
    return this.http.post(url, input, this.options)
      .map((response: Response) => response.json());
  }

  filterVendorBills(url: string, input: any): any {
    return this.http.post(url, input, this.options)
      .map((response: Response) => response.json());
  }

  getThisWeekVendorBills(url: string, input: any): any {
    return this.http.post(url, input, this.options)
      .map((response: Response) => response.json());
  }

  getThisMonthVendorBills(url: string, input: any): any {
    return this.http.post(url, input, this.options)
      .map((response: Response) => response.json());
  }

}


getVendorBillProducts(url: string, input: any): any {
    return this.http.post(url, input, this.options)
      .map((response: Response) => response.json());
  }


saveVendorBillPayment(url: string, input: any): any {
    return this.http.post(url, input, this.options)
      .map((response: Response) => response.json());
  }


deleteVendorBillPayment(url: string, input: any): any {
    return this.http.post(url, input, this.options)
      .map((response: Response) => response.json());
  }


saveVendorBill(url: string, input: any): any {
    return this.http.post(url, input, this.options)
      .map((response: Response) => response.json());
  }


getAllVendorBills(url: string, input: any): any {
    return this.http.post(url, input, this.options)
      .map((response: Response) => response.json());
  }


getVendorBillPayments(url: string, input: any): any {
    return this.http.post(url, input, this.options)
      .map((response: Response) => response.json());
  }


deleteVendorBill(url: string, input: any): any {
    return this.http.post(url, input, this.options)
      .map((response: Response) => response.json());
  }


filterVendorBills(url: string, input: any): any {
    return this.http.post(url, input, this.options)
      .map((response: Response) => response.json());
  }


getThisWeekVendorBills(url: string, input: any): any {
    return this.http.post(url, input, this.options)
      .map((response: Response) => response.json());
  }


getThisMonthVendorBills(url: string, input: any): any {
    return this.http.post(url, input, this.options)
      .map((response: Response) => response.json());
  }


({ factory }: Pick<ts.TransformationContext, "factory">): Factory["create"] => (
  params: Params,
): ts.VariableDeclaration => {
  const node = factory.createVariableDeclaration(params.name, undefined, params.type, params.initializer);
  return node;
}


(
  params: Params,
): ts.VariableDeclaration => {
  const node = factory.createVariableDeclaration(params.name, undefined, params.type, params.initializer);
  return node;
}


(context: Pick<ts.TransformationContext, "factory">): Factory => {
  return {
    create: create(context),
  };
}


export interface Params {
  name: string | ts.BindingName;
  type?: ts.TypeNode;
  initializer?: ts.Expression;
}


export interface Factory {
  create: (params: Params) => ts.VariableDeclaration;
}


export interface DecoratorTestStructure extends BaseTestStructure, NamedTestStructure {
    isDecoratorFactory?: boolean;
    arguments?: ExpressionTestStructure[];
}


(acc: { [keys: string]: Selectable<CandidateBackground_OutsideWorkExperience> }, field: Selectable<CandidateBackground_OutsideWorkExperience>) => {
    acc[field._fieldName] = field;
    return acc;
  }


/**
 * This class represents the entity "CandidateBackground_OutsideWorkExperience" of service "SFOData".
 */
export class CandidateBackground_OutsideWorkExperience extends Entity implements CandidateBackground_OutsideWorkExperienceType {
  /**
   * Technical entity name for CandidateBackground_OutsideWorkExperience.
   */
  static _entityName = 'CandidateBackground_OutsideWorkExperience';
  /**
   * @deprecated Since v1.0.1 Use [[_defaultServicePath]] instead.
   * Technical service name for CandidateBackground_OutsideWorkExperience.
   */
  static _serviceName = 'SFOData';
  /**
   * Default url path for the according service.
   */
  static _defaultServicePath = 'VALUE_IS_UNDEFINED';
  /**
   * Background Element Id.
   */
  backgroundElementId!: BigNumber;
  /**
   * Background Order Position.
   */
  bgOrderPos!: BigNumber;
  /**
   * Type of Business.
   */
  businessType!: string;
  /**
   * Candidate Id.
   */
  candidateId!: BigNumber;
  /**
   * Country.
   */
  country!: string;
  /**
   * Company Name.
   */
  employer!: string;
  /**
   * End Date.
   */
  endDate!: Moment;
  /**
   * Last Modified Date.
   */
  lastModifiedDateTime!: Moment;
  /**
   * Present Employer.
   */
  presentEmployer!: string;
  /**
   * From Date.
   */
  startDate!: Moment;
  /**
   * Title.
   */
  startTitle!: string;
  /**
   * One-to-one navigation property to the [[PicklistOption]] entity.
   */
  businessTypeNav!: PicklistOption;
  /**
   * One-to-one navigation property to the [[Candidate]] entity.
   */
  candidate!: Candidate;
  /**
   * One-to-one navigation property to the [[PicklistOption]] entity.
   */
  countryNav!: PicklistOption;
  /**
   * One-to-one navigation property to the [[PicklistOption]] entity.
   */
  presentEmployerNav!: PicklistOption;

  /**
   * Returns an entity builder to construct instances `CandidateBackground_OutsideWorkExperience`.
   * @returns A builder that constructs instances of entity type `CandidateBackground_OutsideWorkExperience`.
   */
  static builder(): EntityBuilderType<CandidateBackground_OutsideWorkExperience, CandidateBackground_OutsideWorkExperienceTypeForceMandatory> {
    return Entity.entityBuilder(CandidateBackground_OutsideWorkExperience);
  }

  /**
   * Returns a request builder to construct requests for operations on the `CandidateBackground_OutsideWorkExperience` entity type.
   * @returns A `CandidateBackground_OutsideWorkExperience` request builder.
   */
  static requestBuilder(): CandidateBackground_OutsideWorkExperienceRequestBuilder {
    return new CandidateBackground_OutsideWorkExperienceRequestBuilder();
  }

  /**
   * Returns a selectable object that allows the selection of custom field in a get request for the entity `CandidateBackground_OutsideWorkExperience`.
   * @param fieldName Name of the custom field to select
   * @returns A builder that constructs instances of entity type `CandidateBackground_OutsideWorkExperience`.
   */
  static customField(fieldName: string): CustomField<CandidateBackground_OutsideWorkExperience> {
    return Entity.customFieldSelector(fieldName, CandidateBackground_OutsideWorkExperience);
  }

  /**
   * Overwrites the default toJSON method so that all instance variables as well as all custom fields of the entity are returned.
   * @returns An object containing all instance variables + custom fields.
   */
  toJSON(): { [key: string]: any } {
    return { ...this, ...this._customFields };
  }
}


export interface CandidateBackground_OutsideWorkExperienceType {
  backgroundElementId: BigNumber;
  bgOrderPos: BigNumber;
  businessType: string;
  candidateId: BigNumber;
  country: string;
  employer: string;
  endDate: Moment;
  lastModifiedDateTime: Moment;
  presentEmployer: string;
  startDate: Moment;
  startTitle: string;
  businessTypeNav: PicklistOptionType;
  candidate: CandidateType;
  countryNav: PicklistOptionType;
  presentEmployerNav: PicklistOptionType;
}


export interface CandidateBackground_OutsideWorkExperienceTypeForceMandatory {
  backgroundElementId: BigNumber;
  bgOrderPos: BigNumber;
  businessType: string;
  candidateId: BigNumber;
  country: string;
  employer: string;
  endDate: Moment;
  lastModifiedDateTime: Moment;
  presentEmployer: string;
  startDate: Moment;
  startTitle: string;
  businessTypeNav: PicklistOptionType;
  candidate: CandidateType;
  countryNav: PicklistOptionType;
  presentEmployerNav: PicklistOptionType;
}


/**
   * Returns an entity builder to construct instances `CandidateBackground_OutsideWorkExperience`.
   * @returns A builder that constructs instances of entity type `CandidateBackground_OutsideWorkExperience`.
   */
static builder(): EntityBuilderType<CandidateBackground_OutsideWorkExperience, CandidateBackground_OutsideWorkExperienceTypeForceMandatory> {
    return Entity.entityBuilder(CandidateBackground_OutsideWorkExperience);
  }


/**
   * Returns a request builder to construct requests for operations on the `CandidateBackground_OutsideWorkExperience` entity type.
   * @returns A `CandidateBackground_OutsideWorkExperience` request builder.
   */
static requestBuilder(): CandidateBackground_OutsideWorkExperienceRequestBuilder {
    return new CandidateBackground_OutsideWorkExperienceRequestBuilder();
  }


/**
   * Returns a selectable object that allows the selection of custom field in a get request for the entity `CandidateBackground_OutsideWorkExperience`.
   * @param fieldName Name of the custom field to select
   * @returns A builder that constructs instances of entity type `CandidateBackground_OutsideWorkExperience`.
   */
static customField(fieldName: string): CustomField<CandidateBackground_OutsideWorkExperience> {
    return Entity.customFieldSelector(fieldName, CandidateBackground_OutsideWorkExperience);
  }


/**
   * Overwrites the default toJSON method so that all instance variables as well as all custom fields of the entity are returned.
   * @returns An object containing all instance variables + custom fields.
   */
toJSON(): { [key: string]: any } {
    return { ...this, ...this._customFields };
  }


@Module({
  controllers: [MemesController],
  providers: [MemesService],
  imports: [ConfigModule],
})
export class MemesModule {}


function triggerAbortedSignalAfterNumCalls(maxCalls: number): AbortSignal {
  let count = 0;

  const abortSignal: AbortSignal = {
    get aborted(): boolean {
      ++count;

      if (count >= maxCalls) {
        return true;
      }

      return false;
    },
    addEventListener: () => {},
    removeEventListener: () => {},
    onabort: () => {},
    dispatchEvent: () => true
  };

  return abortSignal;
}


async () => {
      let checkpoints: Checkpoint[] = [
        {
          fullyQualifiedNamespace: "not-used-for-this-test",
          consumerGroup: "not-used-for-this-test",
          eventHubName: "not-used-for-this-test",
          offset: 1009,
          sequenceNumber: 1010,
          partitionId: "0"
        },
        {
          fullyQualifiedNamespace: "not-used-for-this-test",
          consumerGroup: "not-used-for-this-test",
          eventHubName: "not-used-for-this-test",
          // this caused a bug for us before - it's a perfectly valid offset
          // but we were thrown off by its falsy-ness. (actually it was
          // sequence number before but the concept is the same)
          offset: 0,
          sequenceNumber: 0,
          partitionId: "1"
        }
      ];

      const checkpointStore: CheckpointStore = {
        claimOwnership: async () => {
          return [];
        },
        listCheckpoints: async () => {
          return checkpoints;
        },
        listOwnership: async () => {
          return [];
        },
        updateCheckpoint: async () => {}
      };

      // we're not actually going to start anything here so there's nothing
      // to stop
      const processor = new EventProcessor(
        EventHubClient.defaultConsumerGroupName,
        client,
        {
          processEvents: async () => {},
          processError: async () => {}
        },
        checkpointStore,
        {
          maxBatchSize: 1,
          maxWaitTimeInSeconds: 1
        }
      );

      // checkpoint is available for partition 0
      let eventPosition = await processor["_getStartingPosition"]("0");
      eventPosition!.offset!.should.equal(1009);
      should.not.exist(eventPosition!.sequenceNumber);

      //checkpoint is available for partition 1
      eventPosition = await processor["_getStartingPosition"]("1");
      eventPosition!.offset!.should.equal(0);
      should.not.exist(eventPosition!.sequenceNumber);

      // no checkpoint available for partition 2
      eventPosition = await processor["_getStartingPosition"]("2");
      should.not.exist(eventPosition);
    }


async () => {
          return [];
        }


async () => {
          return checkpoints;
        }


() => {
      let eventProcessor: EventProcessor;
      let userCallback: (() => void) | undefined;
      let errorFromCallback: Error | undefined;
      let contextFromCallback: PartitionContext | undefined;

      beforeEach(() => {
        userCallback = undefined;
        errorFromCallback = undefined;
        contextFromCallback = undefined;

        // note: we're not starting this event processor so there's nothing to stop()
        // it's only here so we can call a few private methods on it.
        eventProcessor = new EventProcessor(
          EventHubClient.defaultConsumerGroupName,
          client,
          {
            processEvents: async () => {},
            processError: async (err, context) => {
              // simulate the user messing up and accidentally throwing an error
              // we should just log it and not kill anything.
              errorFromCallback = err;
              contextFromCallback = context;

              if (userCallback) {
                userCallback();
              }
            }
          },
          new InMemoryCheckpointStore(),
          defaultOptions
        );
      });

      it("error thrown from user's processError handler", async () => {
        // the user's error handler will throw an error - won't escape from this function
        userCallback = () => {
          throw new Error("Error thrown from the user's error handler");
        };

        await eventProcessor["_handleSubscriptionError"](new Error("test error"));

        errorFromCallback!.message.should.equal("test error");
        contextFromCallback!.partitionId.should.equal("");
      });

      it("non-useful errors are filtered out", async () => {
        // the user's error handler will throw an error - won't escape from this function

        await eventProcessor["_handleSubscriptionError"](new AbortError("test error"));

        // we don't call the user's handler for abort errors
        should.not.exist(errorFromCallback);
        should.not.exist(contextFromCallback);
      });
    }


() => {
        userCallback = undefined;
        errorFromCallback = undefined;
        contextFromCallback = undefined;

        // note: we're not starting this event processor so there's nothing to stop()
        // it's only here so we can call a few private methods on it.
        eventProcessor = new EventProcessor(
          EventHubClient.defaultConsumerGroupName,
          client,
          {
            processEvents: async () => {},
            processError: async (err, context) => {
              // simulate the user messing up and accidentally throwing an error
              // we should just log it and not kill anything.
              errorFromCallback = err;
              contextFromCallback = context;

              if (userCallback) {
                userCallback();
              }
            }
          },
          new InMemoryCheckpointStore(),
          defaultOptions
        );
      }


async (err, context) => {
              // simulate the user messing up and accidentally throwing an error
              // we should just log it and not kill anything.
              errorFromCallback = err;
              contextFromCallback = context;

              if (userCallback) {
                userCallback();
              }
            }


async () => {
        // the user's error handler will throw an error - won't escape from this function
        userCallback = () => {
          throw new Error("Error thrown from the user's error handler");
        };

        await eventProcessor["_handleSubscriptionError"](new Error("test error"));

        errorFromCallback!.message.should.equal("test error");
        contextFromCallback!.partitionId.should.equal("");
      }


() => {
          throw new Error("Error thrown from the user's error handler");
        }


async () => {
        // the user's error handler will throw an error - won't escape from this function

        await eventProcessor["_handleSubscriptionError"](new AbortError("test error"));

        // we don't call the user's handler for abort errors
        should.not.exist(errorFromCallback);
        should.not.exist(contextFromCallback);
      }


async () => {
      const checkpointStore = {
        claimOwnershipCalled: false,

        // the important thing is that the EventProcessor won't be able to claim
        // any partitions, causing it to go down the "I tried but failed" path.
        async claimOwnership(_: PartitionOwnership[]): Promise<PartitionOwnership[]> {
          checkpointStore.claimOwnershipCalled = true;
          return [];
        },

        // (these aren't used for this test)
        async listOwnership(): Promise<PartitionOwnership[]> {
          return [];
        },
        async updateCheckpoint(): Promise<void> {},
        async listCheckpoints(): Promise<Checkpoint[]> {
          return [];
        }
      };

      const pumpManager = {
        createPumpCalled: false,

        async createPump() {
          pumpManager.createPumpCalled = true;
        },

        async removeAllPumps() {}
      };

      const eventProcessor = new EventProcessor(
        EventHubClient.defaultConsumerGroupName,
        client,
        {
          processEvents: async () => {},
          processError: async () => {}
        },
        checkpointStore,
        {
          ...defaultOptions,
          pumpManager: pumpManager
        }
      );

      await eventProcessor["_claimOwnership"]({
        consumerGroup: "cgname",
        eventHubName: "ehname",
        fullyQualifiedNamespace: "fqdn",
        ownerId: "owner",
        partitionId: "0"
      });

      // when we fail to claim a partition we should _definitely_
      // not attempt to start a pump.
      pumpManager.createPumpCalled.should.be.false;

      // we'll attempt to claim a partition (but won't succeed)
      checkpointStore.claimOwnershipCalled.should.be.true;
    }


async () => {
      const commonFields = {
        fullyQualifiedNamespace: "irrelevant namespace",
        eventHubName: "irrelevant eventhub name",
        consumerGroup: "irrelevant consumer group"
      };

      const handlers = new FakeSubscriptionEventHandlers();
      const checkpointStore = new InMemoryCheckpointStore();

      const originalClaimedPartitions = await checkpointStore.claimOwnership([
        // abandoned claim
        { ...commonFields, partitionId: "1001", ownerId: "", etag: "abandoned etag" },
        // normally owned claim
        { ...commonFields, partitionId: "1002", ownerId: "owned partition", etag: "owned etag" }
        // 1003 - completely unowned
      ]);

      originalClaimedPartitions.sort((a, b) => a.partitionId.localeCompare(b.partitionId));

      const fakeEventHubClient = sinon.createStubInstance(EventHubClient);
      const partitionIds = ["1001", "1002", "1003"];

      fakeEventHubClient.getPartitionIds.resolves(partitionIds);
      sinon.replaceGetter(fakeEventHubClient, "eventHubName", () => commonFields.eventHubName);
      sinon.replaceGetter(
        fakeEventHubClient,
        "fullyQualifiedNamespace",
        () => commonFields.fullyQualifiedNamespace
      );

      const ep = new EventProcessor(
        commonFields.consumerGroup,
        fakeEventHubClient as any,
        handlers,
        checkpointStore,
        {
          maxBatchSize: 1,
          loopIntervalInMs: 1,
          maxWaitTimeInSeconds: 1,
          pumpManager: {
            async createPump() {},
            async removeAllPumps(): Promise<void> {}
          }
        }
      );

      // allow three iterations through the loop - one for each partition that
      // we expect to be claimed
      //
      // we'll let one more go through just to make sure we're not going to
      // pick up an extra surprise partition
      //
      // This particular behavior is really specific to the FairPartitionLoadBalancer but that's okay for now.
      const numTimesAbortedIsCheckedInLoop = 3;
      await ep["_runLoopWithLoadBalancing"](
        ep["_processingTarget"] as PartitionLoadBalancer,
        triggerAbortedSignalAfterNumCalls(partitionIds.length * numTimesAbortedIsCheckedInLoop)
      );

      handlers.errors.should.be.empty;

      const currentOwnerships = await checkpointStore.listOwnership(
        commonFields.fullyQualifiedNamespace,
        commonFields.eventHubName,
        commonFields.consumerGroup
      );
      currentOwnerships.sort((a, b) => a.partitionId.localeCompare(b.partitionId));

      currentOwnerships.should.deep.equal([
        {
          ...commonFields,
          partitionId: "1001",
          ownerId: ep.id,
          etag: currentOwnerships[0].etag,
          lastModifiedTimeInMs: currentOwnerships[0].lastModifiedTimeInMs
        },
        // 1002 is not going to be claimed since it's already owned so it should be untouched
        originalClaimedPartitions[1],
        {
          ...commonFields,
          partitionId: "1003",
          ownerId: ep.id,
          etag: currentOwnerships[2].etag,
          lastModifiedTimeInMs: currentOwnerships[2].lastModifiedTimeInMs
        }
      ]);

      // now let's "unclaim" everything by stopping our event processor
      await ep.stop();

      // sanity check - we were previously modifying the original instances
      // in place which...isn't right.
      currentOwnerships.should.deep.equal([
        {
          ...commonFields,
          partitionId: "1001",
          ownerId: ep.id,
          etag: currentOwnerships[0].etag,
          lastModifiedTimeInMs: currentOwnerships[0].lastModifiedTimeInMs
        },
        // 1002 is not going to be claimed since it's already owned so it should be untouched
        originalClaimedPartitions[1],
        {
          ...commonFields,
          partitionId: "1003",
          ownerId: ep.id,
          etag: currentOwnerships[2].etag,
          lastModifiedTimeInMs: currentOwnerships[2].lastModifiedTimeInMs
        }
      ]);

      const ownershipsAfterStop = await checkpointStore.listOwnership(
        commonFields.fullyQualifiedNamespace,
        commonFields.eventHubName,
        commonFields.consumerGroup
      );
      ownershipsAfterStop.sort((a, b) => a.partitionId.localeCompare(b.partitionId));

      ownershipsAfterStop.should.deep.equal([
        {
          ...commonFields,
          partitionId: "1001",
          ownerId: "",
          etag: ownershipsAfterStop[0].etag,
          lastModifiedTimeInMs: ownershipsAfterStop[0].lastModifiedTimeInMs
        },
        // 1002 is not going to be claimed since it's already owned so it should be untouched
        originalClaimedPartitions[1],
        {
          ...commonFields,
          partitionId: "1003",
          ownerId: "",
          etag: ownershipsAfterStop[2].etag,
          lastModifiedTimeInMs: ownershipsAfterStop[2].lastModifiedTimeInMs
        }
      ]);
    }


(a, b) => a.partitionId.localeCompare(b.partitionId)


() => commonFields.eventHubName


() => commonFields.fullyQualifiedNamespace


async () => {
    const errors = [];

    const faultyCheckpointStore: CheckpointStore = {
      listOwnership: async () => [],
      claimOwnership: async () => {
        throw new Error("Some random failure!");
      },
      updateCheckpoint: async () => {},
      listCheckpoints: async () => []
    };

    const eventProcessor = new EventProcessor(
      EventHubClient.defaultConsumerGroupName,
      client,
      {
        processEvents: async () => {},
        processError: async (err, _) => {
          errors.push(err);
        }
      },
      faultyCheckpointStore,
      {
        ...defaultOptions,
        processingTarget: new GreedyPartitionLoadBalancer(["0"])
      }
    );

    // claimOwnership() calls that fail in the runloop of eventProcessor
    // will get directed to the user's processError handler.
    eventProcessor.start();

    try {
      await loopUntil({
        name: "waiting for checkpoint store errors to show up",
        timeBetweenRunsMs: 1000,
        maxTimes: 30,
        until: async () => errors.length !== 0
      });

      errors.length.should.equal(1);
    } finally {
      // this will also fail - we "abandon" all claimed partitions at
      // when a processor is stopped (which requires us to claim them
      // with an empty owner ID).
      //
      // Note that this one gets thrown directly from stop(), rather
      // than reporting to processError() since we have a direct
      // point of contact with the user.
      await eventProcessor.stop().should.be.rejectedWith(/Some random failure!/);
    }
  }


async () => []


async () => {
        throw new Error("Some random failure!");
      }


async (err, _) => {
          errors.push(err);
        }


async () => errors.length !== 0


async () => {
    const errors = new Set<Error>();

    const eventProcessor = new EventProcessor(
      EventHubClient.defaultConsumerGroupName,
      client,
      {
        processClose: async () => {
          throw new Error("processClose() error");
        },
        processEvents: async () => {
          throw new Error("processEvents() error");
        },
        processInitialize: async () => {
          throw new Error("processInitialize() error");
        },
        processError: async (err, _) => {
          errors.add(err);
          throw new Error("These are logged but ignored");
        }
      },
      new InMemoryCheckpointStore(),
      {
        ...defaultOptions,
        processingTarget: new GreedyPartitionLoadBalancer(["0"])
      }
    );

    // errors that occur within the user's own event handlers will get
    // routed to their processError() handler
    eventProcessor.start();

    try {
      await loopUntil({
        name: "waiting for errors thrown from user's handlers",
        timeBetweenRunsMs: 1000,
        maxTimes: 30,
        until: async () => errors.size >= 3
      });

      const messages = [...errors].map((e) => e.message);
      messages.sort();

      messages.should.deep.equal([
        "processClose() error",
        "processEvents() error",
        "processInitialize() error"
      ]);
    } finally {
      await eventProcessor.stop();
    }
  }


async () => {
          throw new Error("processClose() error");
        }


async () => {
          throw new Error("processEvents() error");
        }


async () => {
          throw new Error("processInitialize() error");
        }


async (err, _) => {
          errors.add(err);
          throw new Error("These are logged but ignored");
        }


async () => errors.size >= 3


(e) => e.message


async (context) => context.setStartingPosition(EventPosition.latest())


async (context) => {
          didPartitionProcessorStart = true;
          context.setStartingPosition(EventPosition.latest());
        }


async (event, context) => {}


(id) => checkpointMap.set(id, [])


async () => {
      const checkpointStore = new InMemoryCheckpointStore();
      const allObjects = new Set();

      const assertUnique = (...objects: any[]) => {
        const size = allObjects.size;

        for (const obj of objects) {
          allObjects.add(obj);
          size.should.be.lessThan(allObjects.size);
        }
      };

      const basicProperties = {
        consumerGroup: "initial consumer group",
        eventHubName: "initial event hub name",
        fullyQualifiedNamespace: "initial fully qualified namespace"
      };

      const originalPartitionOwnership = {
        ...basicProperties,
        ownerId: "initial owner ID",
        partitionId: "1001"
      };

      const copyOfPartitionOwnership = {
        ...originalPartitionOwnership
      };

      assertUnique(originalPartitionOwnership);

      for (let i = 0; i < 2; ++i) {
        const ownerships = await checkpointStore.claimOwnership([originalPartitionOwnership]);

        // second sanity check - we were also modifying the input parameter
        // (which was also bad)
        copyOfPartitionOwnership.should.deep.equal(originalPartitionOwnership);

        assertUnique(...ownerships);
      }

      for (let i = 0; i < 2; ++i) {
        const ownerships = await checkpointStore.listOwnership(
          basicProperties.fullyQualifiedNamespace,
          basicProperties.eventHubName,
          basicProperties.consumerGroup
        );
        assertUnique(...ownerships);
      }

      const originalCheckpoint: Checkpoint = {
        ...basicProperties,
        sequenceNumber: 1,
        partitionId: "1",
        offset: 101
      };

      const copyOfOriginalCheckpoint = {
        ...originalCheckpoint
      };

      await checkpointStore.updateCheckpoint(originalCheckpoint);

      // checking that we don't modify input parameters
      copyOfOriginalCheckpoint.should.deep.equal(originalCheckpoint);

      for (let i = 0; i < 2; ++i) {
        const checkpoints = await checkpointStore.listCheckpoints(
          basicProperties.fullyQualifiedNamespace,
          basicProperties.eventHubName,
          basicProperties.consumerGroup
        );
        assertUnique(...checkpoints);
      }
    }


(...objects: any[]) => {
        const size = allObjects.size;

        for (const obj of objects) {
          allObjects.add(obj);
          size.should.be.lessThan(allObjects.size);
        }
      }


(id) => partitionResultsMap.set(id, { events: [], initialized: false })


(event) => event.body


async () => partitionOwnershipArr.size === partitionIds.length