() => ({
        visitor: {
          Program(path: babel.NodePath<babel.types.Program>, context: Context) {
            if (context.builder) {
              return;
            }
            context.builder = {
              component: createJSXLiteComponent(),
            };

            const isImportOrDefaultExport = (node: babel.Node) =>
              types.isExportDefaultDeclaration(node) ||
              types.isImportDeclaration(node);

            const keepStatements = path.node.body.filter((statement) =>
              isImportOrDefaultExport(statement),
            );
            const cutStatements = path.node.body.filter(
              (statement) => !isImportOrDefaultExport(statement),
            );

            // TODO: support multiple? e.g. for others to add imports?
            context.builder.component.hooks.preComponent = generate(
              types.program(cutStatements),
            ).code;

            path.replaceWith(types.program(keepStatements));
          },
          FunctionDeclaration(
            path: babel.NodePath<babel.types.FunctionDeclaration>,
            context: Context,
          ) {
            const { node } = path;
            if (types.isIdentifier(node.id)) {
              const name = node.id.name;
              if (name[0].toUpperCase() === name[0]) {
                path.replaceWith(
                  jsonToAst(componentFunctionToJson(node, context)),
                );
              }
            }
          },
          ImportDeclaration(
            path: babel.NodePath<babel.types.ImportDeclaration>,
            context: Context,
          ) {
            // @jsx-lite/core imports compile away. yeehaw
            if (path.node.source.value == '@jsx-lite/core') {
              path.remove();
              return;
            }
            const importObject: JSXLiteImport = {
              imports: {},
              path: path.node.source.value,
            };
            for (const specifier of path.node.specifiers) {
              if (types.isImportSpecifier(specifier)) {
                importObject.imports[
                  (specifier.imported as babel.types.Identifier).name
                ] = specifier.local.name;
              } else if (types.isImportDefaultSpecifier(specifier)) {
                importObject.imports[specifier.local.name] = 'default';
              } else if (types.isImportNamespaceSpecifier(specifier)) {
                importObject.imports[specifier.local.name] = '*';
              }
            }
            context.builder.component.imports.push(importObject);

            path.remove();
          },
          ExportDefaultDeclaration(
            path: babel.NodePath<babel.types.ExportDefaultDeclaration>,
          ) {
            path.replaceWith(path.node.declaration);
          },
          JSXElement(path: babel.NodePath<babel.types.JSXElement>) {
            const { node } = path;
            path.replaceWith(jsonToAst(jsxElementToJson(node)));
          },
        },
      })


(node: babel.Node) =>
              types.isExportDefaultDeclaration(node) ||
              types.isImportDeclaration(node)


(statement) =>
              isImportOrDefaultExport(statement)


(statement) => !isImportOrDefaultExport(statement)


type Context = {
  // Babel has other context
  builder: {
    component: JSXLiteComponent;
  };
};


Program(path: babel.NodePath<babel.types.Program>, context: Context) {
            if (context.builder) {
              return;
            }
            context.builder = {
              component: createJSXLiteComponent(),
            };

            const isImportOrDefaultExport = (node: babel.Node) =>
              types.isExportDefaultDeclaration(node) ||
              types.isImportDeclaration(node);

            const keepStatements = path.node.body.filter((statement) =>
              isImportOrDefaultExport(statement),
            );
            const cutStatements = path.node.body.filter(
              (statement) => !isImportOrDefaultExport(statement),
            );

            // TODO: support multiple? e.g. for others to add imports?
            context.builder.component.hooks.preComponent = generate(
              types.program(cutStatements),
            ).code;

            path.replaceWith(types.program(keepStatements));
          }


FunctionDeclaration(
            path: babel.NodePath<babel.types.FunctionDeclaration>,
            context: Context,
          ) {
            const { node } = path;
            if (types.isIdentifier(node.id)) {
              const name = node.id.name;
              if (name[0].toUpperCase() === name[0]) {
                path.replaceWith(
                  jsonToAst(componentFunctionToJson(node, context)),
                );
              }
            }
          }


ImportDeclaration(
            path: babel.NodePath<babel.types.ImportDeclaration>,
            context: Context,
          ) {
            // @jsx-lite/core imports compile away. yeehaw
            if (path.node.source.value == '@jsx-lite/core') {
              path.remove();
              return;
            }
            const importObject: JSXLiteImport = {
              imports: {},
              path: path.node.source.value,
            };
            for (const specifier of path.node.specifiers) {
              if (types.isImportSpecifier(specifier)) {
                importObject.imports[
                  (specifier.imported as babel.types.Identifier).name
                ] = specifier.local.name;
              } else if (types.isImportDefaultSpecifier(specifier)) {
                importObject.imports[specifier.local.name] = 'default';
              } else if (types.isImportNamespaceSpecifier(specifier)) {
                importObject.imports[specifier.local.name] = '*';
              }
            }
            context.builder.component.imports.push(importObject);

            path.remove();
          }


ExportDefaultDeclaration(
            path: babel.NodePath<babel.types.ExportDefaultDeclaration>,
          ) {
            path.replaceWith(path.node.declaration);
          }


JSXElement(path: babel.NodePath<babel.types.JSXElement>) {
            const { node } = path;
            path.replaceWith(jsonToAst(jsxElementToJson(node)));
          }


(key, value, success, reject?) => {
        this.storage.get('userForm').then(userFormStrng => {
            let userForm: any;
            try {
                userForm = JSON.parse(userFormStrng);
                userForm[key] = value;
                this.storage.set('userForm', JSON.stringify(userForm))
                    .then(res => {
                        this.setCacheUser(JSON.parse(res));
                        success(this._cache_user);
                    })
                    .catch(error => reject(error));
                success(userForm);
            } catch (e) {
                this.storage.clear().then(() => {
                    console.log('store cleared');
                    this.storage.set('userForm', JSON.stringify({ [key]: value }))
                        .then(res => success(res))
                        .catch(error => reject(error));
                })
            }
        }).catch(err => {
            this.generateEmptyUserForm((res) => success(res), null, { [key]: value });
        });
    }


userFormStrng => {
            let userForm: any;
            try {
                userForm = JSON.parse(userFormStrng);
                userForm[key] = value;
                this.storage.set('userForm', JSON.stringify(userForm))
                    .then(res => {
                        this.setCacheUser(JSON.parse(res));
                        success(this._cache_user);
                    })
                    .catch(error => reject(error));
                success(userForm);
            } catch (e) {
                this.storage.clear().then(() => {
                    console.log('store cleared');
                    this.storage.set('userForm', JSON.stringify({ [key]: value }))
                        .then(res => success(res))
                        .catch(error => reject(error));
                })
            }
        }


res => {
                        this.setCacheUser(JSON.parse(res));
                        success(this._cache_user);
                    }


() => {
                    console.log('store cleared');
                    this.storage.set('userForm', JSON.stringify({ [key]: value }))
                        .then(res => success(res))
                        .catch(error => reject(error));
                }


res => success(res)


err => {
            this.generateEmptyUserForm((res) => success(res), null, { [key]: value });
        }


(res) => success(res)


(success, reject?) => {
        try {
            this.storage.get('userForm').then(res => {
                let data = JSON.parse(res);
                if (data && data.type) success(data.type);
            }).catch(err => {
                reject(false)
            })
        } catch (e) {
            reject(false)
        }

    }


res => {
                let data = JSON.parse(res);
                if (data && data.type) success(data.type);
            }


err => {
                reject(false)
            }


(cb) => {
        this.storage.clear().then(res => {
            this.storage.keys().then((keys) => {
                cb(keys);
            });
        });

    }


res => {
            this.storage.keys().then((keys) => {
                cb(keys);
            });
        }


(keys) => {
                cb(keys);
            }


(success, reject?, initData?) => {
        let err: any;
        let data: any;
        (initData) ? data = initData : data = {};
        this.storage.set('userForm', JSON.stringify(data)).then(res => success(res)).catch(error => err = error);
        if (reject) reject(err);
    }


error => err = error


(userObj: Object) => {
        console.log('cache user ', this._cache_user);
        this._cache_user = userObj;
        console.log('cache user ', this._cache_user);
    }


@Injectable()
export class Localstorage {

    private _cache_user: any;
    user = "nothing";

    constructor(private storage: Storage) { }

    saveUsertype(User) {
        localStorage.setItem(this.user, User);
    }
    public addItemToUserForm = (key, value, success, reject?) => {
        this.storage.get('userForm').then(userFormStrng => {
            let userForm: any;
            try {
                userForm = JSON.parse(userFormStrng);
                userForm[key] = value;
                this.storage.set('userForm', JSON.stringify(userForm))
                    .then(res => {
                        this.setCacheUser(JSON.parse(res));
                        success(this._cache_user);
                    })
                    .catch(error => reject(error));
                success(userForm);
            } catch (e) {
                this.storage.clear().then(() => {
                    console.log('store cleared');
                    this.storage.set('userForm', JSON.stringify({ [key]: value }))
                        .then(res => success(res))
                        .catch(error => reject(error));
                })
            }
        }).catch(err => {
            this.generateEmptyUserForm((res) => success(res), null, { [key]: value });
        });
    }

    public getProductType = (success, reject?) => {
        try {
            this.storage.get('userForm').then(res => {
                let data = JSON.parse(res);
                if (data && data.type) success(data.type);
            }).catch(err => {
                reject(false)
            })
        } catch (e) {
            reject(false)
        }

    }

    public wipeStorageFromUserData = (cb) => {
        this.storage.clear().then(res => {
            this.storage.keys().then((keys) => {
                cb(keys);
            });
        });

    }

    private generateEmptyUserForm = (success, reject?, initData?) => {
        let err: any;
        let data: any;
        (initData) ? data = initData : data = {};
        this.storage.set('userForm', JSON.stringify(data)).then(res => success(res)).catch(error => err = error);
        if (reject) reject(err);
    }

    private setCacheUser = (userObj: Object) => {
        console.log('cache user ', this._cache_user);
        this._cache_user = userObj;
        console.log('cache user ', this._cache_user);
    }

}


saveUsertype(User) {
        localStorage.setItem(this.user, User);
    }


(value: string) => {
  let isSpecialArryString = false;
  const typeOfValue = typeof value;
  if (typeOfValue === 'string' && value[0] == '[' && value[value.length - 1] == ']') {
    const convertedValue = parseArray(value);
    if (Array.isArray(convertedValue)) {
      convertedValue.forEach((item: any) => {
        if (!isNaN(Number(item)) && typeof item === 'string' && item.includes('.')) {
          const spl = item.split('.');
          if (spl.length > 1) {
            //Also, making sure that we don't touch if the values are ["0.12"]
            if (spl[0].length > 1 && Number(spl[0]) === 0) {
              isSpecialArryString = true;
            }
          }
        }
      });
    }
  }
  return isSpecialArryString;
}


(item: any) => {
        if (!isNaN(Number(item)) && typeof item === 'string' && item.includes('.')) {
          const spl = item.split('.');
          if (spl.length > 1) {
            //Also, making sure that we don't touch if the values are ["0.12"]
            if (spl[0].length > 1 && Number(spl[0]) === 0) {
              isSpecialArryString = true;
            }
          }
        }
      }


(value: any, allowedValues?: string[]): CapiVariableTypes => {
  if (allowedValues) {
    if (allowedValues.every((v) => typeof v === 'string')) {
      return CapiVariableTypes.ENUM;
    }
  }
  if (typeof value === 'boolean' || value === 'true' || value === 'false') {
    return CapiVariableTypes.BOOLEAN;
  }
  if (typeof value === 'number') {
    return CapiVariableTypes.NUMBER;
  }
  if (Array.isArray(value) || isStringArray(value)) {
    // if the array contains values like ["00.12"] then they need special handling as it's not same as ["0.12"] so
    //the CAPI type needs to be set to CapiVariableTypes.STRING instead of CapiVariableTypes.ARRAY otherwise SIM throws an errors.
    if (typeof value === 'string' && isSpecialArrayString(value)) {
      return CapiVariableTypes.STRING;
    }
    return CapiVariableTypes.ARRAY;
  }
  if (typeof value === 'string') {
    return CapiVariableTypes.STRING;
  }

  return CapiVariableTypes.UNKNOWN;
}


(v) => typeof v === 'string'


(
  value: any,
  capiType: CapiVariableTypes,
  allowedValues?: string[] | null,
  shouldConvertNumbers?: boolean,
) => {
  switch (capiType) {
    case CapiVariableTypes.NUMBER:
      if (!isNaN(parseFloat(value))) {
        return `${parseFloat(value)}`;
      }
      break;
    case CapiVariableTypes.STRING:
    case CapiVariableTypes.MATH_EXPR:
      return `${value}`;
    case CapiVariableTypes.ENUM:
      if (allowedValues && Array.isArray(allowedValues)) {
        if (!allowedValues.includes(value)) {
          throw new Error(
            `Attempting to assign an invalid value to an ENUM ${value} | Allowed: ${JSON.stringify(
              allowedValues,
            )}`,
          );
        }
      }
      return `${value}`;
    case CapiVariableTypes.BOOLEAN:
      return String(parseBoolean(value)); // for some reason these need to be strings
    case CapiVariableTypes.ARRAY:
    case CapiVariableTypes.ARRAY_POINT:
      return shouldConvertNumbers ? parseArray(value) : parseArrayWithoutStringConversion(value);
    default:
      return `${value}`;
  }

  return value;
}


(capiVar: CapiVariable): any => {
  switch (capiVar.type) {
    case CapiVariableTypes.BOOLEAN:
      return parseBoolean(capiVar.value);
    case CapiVariableTypes.NUMBER:
      if (!isNaN(parseFloat(capiVar.value))) {
        return parseFloat(capiVar.value);
      }
      return capiVar.value;
    case CapiVariableTypes.ARRAY:
    case CapiVariableTypes.ARRAY_POINT:
      return parseArray(capiVar.value);
    default:
      return capiVar.value;
  }
}


export class CapiVariable {
  public key: string;
  public type: CapiVariableTypes;
  public value: any;
  public readonly: boolean;
  public writeonly: boolean;
  public allowedValues: string[] | null;
  public bindTo: string | null;
  public shouldConvertNumbers?: boolean;
  constructor(options: ICapiVariableOptions) {
    this.key = options.key;
    this.type = options.type || CapiVariableTypes.UNKNOWN;
    this.readonly = !!options.readonly;
    this.writeonly = !!options.writeonly;
    this.allowedValues = Array.isArray(options.allowedValues) ? options.allowedValues : null;
    this.bindTo = options.bindTo || null;
    this.shouldConvertNumbers =
      options.shouldConvertNumbers === undefined ? true : options.shouldConvertNumbers;
    if (this.type === CapiVariableTypes.UNKNOWN) {
      this.type = getCapiType(options.value);
    }
    this.value = coerceCapiValue(
      options.value,
      this.type,
      this.allowedValues,
      this.shouldConvertNumbers,
    );
  }
}


export interface ICapiVariableOptions {
  key: string;
  type?: CapiVariableTypes;
  value?: any;
  readonly?: boolean;
  writeonly?: boolean;
  allowedValues?: string[];
  bindTo?: string;
  shouldConvertNumbers?: boolean;
}


export interface JanusConditionProperties extends ConditionProperties {
  id: string;
  type?: CapiVariableTypes;
}


export enum CapiVariableTypes {
  NUMBER = 1,
  STRING = 2,
  ARRAY = 3,
  BOOLEAN = 4,
  ENUM = 5,
  MATH_EXPR = 6,
  ARRAY_POINT = 7,
  // TODO: Add OBJECT (janus-script can support)
  UNKNOWN = 99,
}


function createWindow() {

  const electronScreen = screen;
  const size = electronScreen.getPrimaryDisplay().workAreaSize;

  // Create the browser window.
  win = new BrowserWindow({
    x: 0,
    y: 0,
    width: size.width,
    height: size.height,
    webPreferences: {
      nodeIntegration: true,
    },
  });

  if (serve) {
    require('electron-reload')(__dirname, {
      electron: require(`${__dirname}/node_modules/electron`)
    });
    win.loadURL('http://localhost:4200');
  } else {
    win.loadURL(url.format({
      pathname: path.join(__dirname, 'dist/index.html'),
      protocol: 'file:',
      slashes: true
    }));
  }

  if (serve) {
    win.webContents.openDevTools();
  }

  init();

  // Emitted when the window is closed.
  win.on('closed', () => {
    // Dereference the window object, usually you would store window
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    win = null;
    deinit();
  });

}


() => {
    // Dereference the window object, usually you would store window
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    win = null;
    deinit();
  }


(type: 'red' | 'green' | 'blue') => {
  switch (type) {
    case 'red':
      return styles.typedBackgroundRed
    case 'green':
      return styles.typedBackgroundGreen
    case 'blue':
      return styles.typedBackgroundBlue
    default:
      Flow.ifFlowComplainsAboutThisFunctionYouHaventHandledAllCasesInASwitch(type)
      return styles.typedBackgroundRed
  }
}


p => (
  <Kb.Box2 direction="vertical"


p => {
  let doWhat: string
  switch (p.service) {
    case 'phone':
      doWhat = 'verify their phone number'
      break
    case 'email':
      doWhat = 'verify their e-mail address'
      break
    default:
      doWhat = `prove their ${upperFirst(p.service)}`
      break
  }
  const url = 'https://keybase.io/install'
  return (
    <>
      <Kb.Text type="BodySmall" style={styles.proveIt}>
        Tell {p.fullName || p.name} to join Keybase and {doWhat}.
      </Kb.Text>
      <Kb.Text type="BodySmall" style={styles.proveIt}>
        Send them this link:{' '}
        <Kb.Text type="BodySmallPrimaryLink" onClickURL={url} selectable={true}>
          {url}
        </Kb.Text>
      </Kb.Text>
    </>


p => {
  let assertions: React.ReactNode
  if (p.assertionKeys) {
    assertions = [
      ...p.assertionKeys.map(a => <Assertion key={a} username={p.username} assertionKey={a} />),
      ...(p.suggestionKeys || []).map(s => (
        <Assertion isSuggestion={true} key={s} username={p.username} assertionKey={s} />
      )),
    ]
  } else {
    assertions = null
  }

  return (
    <Kb.Box2 direction="vertical" fullWidth={true}>
      {assertions}
      {!!p.notAUser && !!p.service && <ProveIt {...p} />}
    </Kb.Box2>


a => <Assertion key


s => (
        <Assertion isSuggestion={true}


() => this.props.onChangeFollowing(true)


() => this.props.onChangeFollowing(false)


following => (
    <Kb.ClickableBox
      onClick={following ? this._onClickFollowing : this._onClickFollowers}


width => {
  const singleItemWidth = Styles.isMobile ? 130 : 120
  const itemsInARow = Math.floor(Math.max(1, width / singleItemWidth))
  const itemWidth = Math.floor(width / itemsInARow)
  return {itemWidth, itemsInARow}
}


u => (
          <Friend key={u}


(following: boolean) => {
    this.setState(p => {
      if (p.selectedFollowing === following) {
        return null
      }
      const selectedFollowing = !p.selectedFollowing
      usernameSelectedFollowing[this.props.username] = selectedFollowing
      return {selectedFollowing}
    })
  }


p => {
      if (p.selectedFollowing === following) {
        return null
      }
      const selectedFollowing = !p.selectedFollowing
      usernameSelectedFollowing[this.props.username] = selectedFollowing
      return {selectedFollowing}
    }


({section}) => {
    if (section === this._bioTeamProofsSection) return null
    if (this.props.notAUser) return null

    const loading = !this.props.followers || !this.props.following
    return (
      <FriendshipTabs
        key="tabs"
        loading={loading}
        numFollowers={this.props.followersCount}
        numFollowing={this.props.followingCount}
        onChangeFollowing={this._changeFollowing}
        selectedFollowing={this.state.selectedFollowing}
      />
    )
  }


({item, section, index}) =>
    this.props.notAUser ? null : item.type === 'noFriends' || item.type === 'loading' ? (
      <Kb.Box2 direction="horizontal"


() => (
      <BioTeamProofs
        onAddIdentity={this.props.onAddIdentity}


width => this.setState(p => (p.width !== width ? {width} : null))


p => (p.width !== width ? {width} : null)


e => e.code !== RPCTypes.StatusCode.scresolutionfailed


class FriendshipTabs extends React.Component<FriendshipTabsProps> {
  _onClickFollowing = () => this.props.onChangeFollowing(true)
  _onClickFollowers = () => this.props.onChangeFollowing(false)
  _tab = following => (
    <Kb.ClickableBox
      onClick={following ? this._onClickFollowing : this._onClickFollowers}
      style={Styles.collapseStyles([
        styles.followTab,
        following === this.props.selectedFollowing && styles.followTabSelected,
      ])}
    >
      <Kb.Text
        type="BodySmallSemibold"
        style={
          following === this.props.selectedFollowing ? styles.followTabTextSelected : styles.followTabText
        }


class FriendRow extends React.Component<FriendRowProps> {
  shouldComponentUpdate(nextProps: FriendRowProps) {
    return (
      this.props.itemWidth !== nextProps.itemWidth || !shallowEqual(this.props.usernames, nextProps.usernames)
    )
  }

  render() {
    return (
      <Kb.Box2 direction="horizontal" fullWidth={true} style={styles.friendRow}>
        {this.props.usernames.map(u => (
          <Friend key={u} username={u} width={this.props.itemWidth} />
        ))}
      </Kb.Box2>
    )
  }
}


export class BioTeamProofs extends React.PureComponent<BioTeamProofsProps> {
  render() {
    const addIdentity = this.props.onAddIdentity ? (
      <Kb.ButtonBar style={styles.addIdentityContainer}>
        <Kb.Button
          fullWidth={true}
          onClick={this.props.onAddIdentity}
          style={styles.addIdentityButton}
          mode="Secondary"
          label="Add more identities"
        >
          <Kb.Meta backgroundColor={Styles.globalColors.blue} title="NEW" style={styles.newMeta} />
        </Kb.Button>
      </Kb.ButtonBar>
    ) : null
    return Styles.isMobile ? (
      <Kb.Box2 direction="vertical" fullWidth={true} style={styles.bioAndProofs}>
        {!!this.props.reason && (
          <Kb.Text
            type="BodySmallSemibold"
            negative={true}
            center={true}
            style={Styles.collapseStyles([styles.reason, colorTypeToStyle(this.props.backgroundColorType)])}
          >
            {this.props.reason}
          </Kb.Text>
        )}


class User extends React.Component<Props, State> {
  constructor(props: Props) {
    super(props)
    this.state = {
      selectedFollowing: !!usernameSelectedFollowing[props.username],
      width: Styles.dimensionWidth,
    }
  }

  _changeFollowing = (following: boolean) => {
    this.setState(p => {
      if (p.selectedFollowing === following) {
        return null
      }
      const selectedFollowing = !p.selectedFollowing
      usernameSelectedFollowing[this.props.username] = selectedFollowing
      return {selectedFollowing}
    })
  }

  _renderSectionHeader = ({section}) => {
    if (section === this._bioTeamProofsSection) return null
    if (this.props.notAUser) return null

    const loading = !this.props.followers || !this.props.following
    return (
      <FriendshipTabs
        key="tabs"
        loading={loading}
        numFollowers={this.props.followersCount}
        numFollowing={this.props.followingCount}
        onChangeFollowing={this._changeFollowing}
        selectedFollowing={this.state.selectedFollowing}
      />
    )
  }

  _renderOtherUsers = ({item, section, index}) =>
    this.props.notAUser ? null : item.type === 'noFriends' || item.type === 'loading' ? (
      <Kb.Box2 direction="horizontal" style={styles.textEmpty} centerChildren={true}>
        <Kb.Text type="BodySmall"


export type BackgroundColorType = 'red' | 'green' | 'blue'


export type Props = {
  assertionKeys: Array<string> | null
  backgroundColorType: BackgroundColorType
  followThem: boolean
  followers: Array<string> | null
  followersCount: number
  following: Array<string> | null
  followingCount: number
  notAUser: boolean
  onAddIdentity: (() => void) | null
  onBack: () => void
  onReload: () => void
  onSearch: () => void
  onEditAvatar: (() => void) | null
  reason: string
  showAirdropBanner: boolean
  state: Types.DetailsState
  suggestionKeys: Array<string> | null
  userIsYou: boolean
  username: string
  name: string // assertion value
  service: string // assertion key (if SBS)
  fullName: string | null // full name from external profile
  title: string
}


type FriendshipTabsProps = {
  loading: boolean
  onChangeFollowing: (arg0: boolean) => void
  selectedFollowing: boolean
  numFollowers: number
  numFollowing: number
}


type FriendRowProps = {
  usernames: Array<string>
  itemWidth: number
}


export type BioTeamProofsProps = {
  onAddIdentity: (() => void) | null
  assertionKeys: Array<string> | null
  backgroundColorType: BackgroundColorType
  onEditAvatar: (() => void) | null
  notAUser: boolean
  suggestionKeys: Array<string> | null
  username: string
  reason: string
  name: string
  service: string
  fullName: string | null
  title: string
}


type State = {
  selectedFollowing: boolean
  width: number
}


shouldComponentUpdate(nextProps: FriendRowProps) {
    return (
      this.props.itemWidth !== nextProps.itemWidth || !shallowEqual(this.props.usernames, nextProps.usernames)
    )
  }


render() {
    return (
      <Kb.Box2 direction="horizontal" fullWidth={true} style={styles.friendRow}>
        {this.props.usernames.map(u => (
          <Friend key={u} username={u} width={this.props.itemWidth} />
        ))}
      </Kb.Box2>
    )
  }


render() {
    const addIdentity = this.props.onAddIdentity ? (
      <Kb.ButtonBar style={styles.addIdentityContainer}>
        <Kb.Button
          fullWidth={true}
          onClick={this.props.onAddIdentity}
          style={styles.addIdentityButton}
          mode="Secondary"
          label="Add more identities"
        >
          <Kb.Meta backgroundColor={Styles.globalColors.blue} title="NEW" style={styles.newMeta} />
        </Kb.Button>
      </Kb.ButtonBar>
    ) : null
    return Styles.isMobile ? (
      <Kb.Box2 direction="vertical" fullWidth={true} style={styles.bioAndProofs}>
        {!!this.props.reason && (
          <Kb.Text
            type="BodySmallSemibold"
            negative={true}
            center={true}
            style={Styles.collapseStyles([styles.reason, colorTypeToStyle(this.props.backgroundColorType)])}
          >
            {this.props.reason}
          </Kb.Text>
        )}


(cxt) => {
    // 获取嵌套页面最后一个的 layout
    let layout = getMatchedComponents(cxt.route).reverse()[0].options.layout;
    if (typeof layout === 'function') {
      layout = layout(cxt);
    }
    return layout;
  }


@Component({
  layout: (cxt) => {
    // 获取嵌套页面最后一个的 layout
    let layout = getMatchedComponents(cxt.route).reverse()[0].options.layout;
    if (typeof layout === 'function') {
      layout = layout(cxt);
    }
    return layout;
  },
})
export default class VueNestedLayout extends Vue {}


async function run() {
  const number =
    context?.payload?.pull_request?.number ||
    +core.getInput("number", { required: false });
  if (isNaN(number) || number < 1) {
    core.info("no numbers given: skip step");
    return;
  }

  try {
    const repo = context.repo;
    const message = core.getInput("message", { required: false });
    const path = core.getInput("path", { required: false });
    const header = core.getInput("header", { required: false }) || "";
    const append = core.getInput("append", { required: false }) || false;
    const recreate = core.getInput("recreate", { required: false }) || false;
    const deleteOldComment = core.getInput("delete", { required: false }) || false;
    const githubToken = core.getInput("GITHUB_TOKEN", { required: true });
    const octokit = new GitHub(githubToken);
    const previous = await findPreviousComment(octokit, repo, number, header);

    if (!message && !path) {
      throw { message: 'Either message or path input is required' };
    }

    if (deleteOldComment && recreate) {
      throw { message: 'delete and recreate cannot be both set to true' };
    }

    let body;

    if (path) {
      body = readFileSync(path, 'utf-8');
    } else {
      body = message;
    }

    if (previous) {
      const previousBody = append && previous.body;
      if (deleteOldComment) {
        await deleteComment(octokit, repo, previous.id);
      } else if (recreate) {
        await deleteComment(octokit, repo, previous.id);
        await createComment(octokit, repo, number, body, header, previousBody);
      } else {
        await updateComment(octokit, repo, previous.id, body, header, previousBody);
      }
    } else {
      await createComment(octokit, repo, number, body, header);
    }
  } catch ({ message }) {
    core.setFailed(message);
  }
}


export class SelectionTool implements Tool {
    mouseUp():string {
        return "Selection tool: mouse up";
    }

    mouseDown():string {
        return "Selection tool: mouse down";
    }
}


mouseUp():string {
        return "Selection tool: mouse up";
    }


mouseDown():string {
        return "Selection tool: mouse down";
    }


interface DuplexifyConstructor {
  (writable?: stream.Writable, readable?: stream.Readable, streamOptions?: stream.DuplexOptions): duplexify.Duplexify;
  new (writable?: stream.Writable, readable?: stream.Readable, streamOptions?: stream.DuplexOptions): duplexify.Duplexify;
}


interface Duplexify extends stream.Duplex {
        setWritable(writable: stream.Writable): void;
        setReadable(readable: stream.Readable): void;
    }


() => {
  return (
    <>
      <Head>
        <title>Pomodoro Clock</title>
      </Head>

      <Container>
        <Title appName={'Pomodoro Clock'} />
        <Clock />
      </Container>
    </>


() => {
  container = render(<Header />).container;
}


() => {
  expect(getByTestId(container, 'logo')).toBeTruthy();
}


() => {
  expect(getByTestId(container, 'search')).toBeTruthy();
}


() => {
  expect(getByTestId(container, 'menu')).toBeTruthy();
}


() => {
  expect(getByTestId(container, 'home-type')).toBeTruthy();
  expect(getByTestId(container, 'dates')).toBeTruthy();
  expect(getByTestId(container, 'guests')).toBeTruthy();
  expect(getByTestId(container, 'price')).toBeTruthy();
  expect(getByTestId(container, 'rooms')).toBeTruthy();
  expect(getByTestId(container, 'amenities')).toBeTruthy();
}


() => {
  let page: AppPage;

  beforeEach(() => {
    page = new AppPage();
  });

  it('should display welcome message', () => {
    page.navigateTo();
    expect(page.getParagraphText()).toEqual('Welcome to base-application!');
  });
}


() => {
    page.navigateTo();
    expect(page.getParagraphText()).toEqual('Welcome to base-application!');
  }


export interface StatusHistoryData {
  items: StatusHistory[];
  search_criteria: SearchCriteria;
  total_count: number;
}


export interface StatusHistory {
  comment: string;
  created_at: Date;
  entity_id?: number;
  entity_name?: string;
  extension_attributes?: ParentItemClass;
  is_customer_notified?: number;
  is_visible_on_front?: number;
  parent_id?: number;
  status: OrderState | string;
}


dataPoint => dataPoint.timestamp.isSame(today, 'd')


dataPoint => dataPoint.timestamp.isSame(yesterday, 'd')


dataPoint => dataPoint.timestamp.isAfter(weekAgo)


@Pipe({
  name: 'relativeTimesFilter'
})
export class RelativeTimesFilterPipe implements PipeTransform {

  transform(timedData: Array<any>, relativeTime: string): any {
    let filteredData: Array<any>;
    switch (relativeTime) {
      case 'today':
        const today = moment();
        filteredData = timedData.filter(dataPoint => dataPoint.timestamp.isSame(today, 'd'));
        break;
      case 'yesterday':
        const yesterday = moment().subtract(1, 'days');
        filteredData = timedData.filter(dataPoint => dataPoint.timestamp.isSame(yesterday, 'd'));
        break;
      case 'last week':
        const weekAgo = moment().subtract(7, 'days');
        filteredData = timedData.filter(dataPoint => dataPoint.timestamp.isAfter(weekAgo));
        break;
      case 'all':
        filteredData = timedData;
        break;
      default:
        filteredData = [];
    }

    return filteredData;
  }

}


transform(timedData: Array<any>, relativeTime: string): any {
    let filteredData: Array<any>;
    switch (relativeTime) {
      case 'today':
        const today = moment();
        filteredData = timedData.filter(dataPoint => dataPoint.timestamp.isSame(today, 'd'));
        break;
      case 'yesterday':
        const yesterday = moment().subtract(1, 'days');
        filteredData = timedData.filter(dataPoint => dataPoint.timestamp.isSame(yesterday, 'd'));
        break;
      case 'last week':
        const weekAgo = moment().subtract(7, 'days');
        filteredData = timedData.filter(dataPoint => dataPoint.timestamp.isAfter(weekAgo));
        break;
      case 'all':
        filteredData = timedData;
        break;
      default:
        filteredData = [];
    }

    return filteredData;
  }


props => props.theme.colors.primary


props =>
      props.theme.mode === 'retro' &&
      "url('/images/retro/cursor/pointer.cur'), pointer"


props => (props.theme.mode === 'retro' ? '1.05em' : '1.25em')


props => props.theme.colors.text


props => props.theme.colors.background


props =>
      props.theme.mode === 'retro' ? 'url(/images/retro/space.gif)' : 'none'


props =>
      props.theme.mode === 'retro' &&
      "url('/images/retro/cursor/default.cur'), default"


props =>
      props.theme.mode === 'retro' &&
      `
      margin-left: 8px;
      letter-spacing: 0.0625rem;
      text-shadow:
        -1px 1px violet,
        -2px 2px indigo,
        -3px 3px blue,
        -4px 4px green,
        -5px 5px yellow,
        -6px 6px orange,
        -7px 7px red;
    `


props =>
      props.theme.mode === 'retro' &&
      "url('/images/retro/cursor/text.cur'), text"


props => props.theme.colors.altBackground


props => {
      const { primary, altBackground } = props.theme.colors

      return `${primary} ${altBackground}`
    }


({ children }) => {
    const SocialMedia = (
        <>
            <a
                className="icon"
                href="https://www.linkedin.com/in/christophervachon/"
                target="_blank"
                title="Checkout my LinkedIn Profile!"
            >
                <FaLinkedin />
            </a>
            <a
                className="icon"
                href="https://twitter.com/CodeVachon"
                target="_blank"
                title="Checkout my Twitter Profile!"
            >
                <FaTwitter />
            </a>
            <a
                className="icon"
                href="https://www.npmjs.com/~codevachon"
                target="_blank"
                title="Checkout my NPM Profile!"
            >
                <FaNpm />
            </a>
            <a
                className="icon"
                href="https://github.com/CodeVachon"
                target="_blank"
                title="Checkout my GitHub Profile!"
            >
                <FaGithub />
            </a>
        </>


() => {
      const sameName = loader.projects().filter(project => project.config.name === this.project.name);
      let uniqueProjectNamePathSegment: string;
      if (sameName.length > 1)
        uniqueProjectNamePathSegment = this.project.name + (sameName.indexOf(this._projectImpl) + 1);
      else
        uniqueProjectNamePathSegment = this.project.name;

      const relativeTestFilePath = path.relative(this.project.testDir, test._requireFile.replace(/\.(spec|test)\.(js|ts|mjs)$/, ''));
      const sanitizedRelativePath = relativeTestFilePath.replace(process.platform === 'win32' ? new RegExp('\\\\', 'g') : new RegExp('/', 'g'), '-');
      const fullTitleWithoutSpec = test.titlePath().slice(1).join(' ') + (test._type === 'test' ? '' : '-worker' + this.workerIndex);

      let testOutputDir = sanitizedRelativePath + '-' + sanitizeForFilePath(trimLongString(fullTitleWithoutSpec));
      if (uniqueProjectNamePathSegment)
        testOutputDir += '-' + sanitizeForFilePath(uniqueProjectNamePathSegment);
      if (this.retry)
        testOutputDir += '-retry' + this.retry;
      if (this.repeatEachIndex)
        testOutputDir += '-repeat' + this.repeatEachIndex;
      return path.join(this.project.outputDir, testOutputDir);
    }


project => project.config.name === this.project.name


() => {
      const relativeTestFilePath = path.relative(this.project.testDir, test._requireFile);
      return path.join(this.project.snapshotDir, relativeTestFilePath + '-snapshots');
    }


class SkipError extends Error {
}


private _modifier(type: 'skip' | 'fail' | 'fixme' | 'slow', modifierArgs: [arg?: any, description?: string]) {
    if (typeof modifierArgs[1] === 'function') {
      throw new Error([
        'It looks like you are calling test.skip() inside the test and pass a callback.',
        'Pass a condition instead and optional description instead:',
        `test('my test', async ({ page, isMobile }) => {`,
        `  test.skip(isMobile, 'This test is not applicable on mobile');`,
        `});`,
      ].join('\n'));
    }

    if (modifierArgs.length >= 1 && !modifierArgs[0])
      return;

    const description = modifierArgs[1];
    this.annotations.push({ type, description });
    if (type === 'slow') {
      this.setTimeout(this.timeout * 3);
    } else if (type === 'skip' || type === 'fixme') {
      this.expectedStatus = 'skipped';
      throw new SkipError('Test is skipped: ' + (description || ''));
    } else if (type === 'fail') {
      if (this.expectedStatus !== 'skipped')
        this.expectedStatus = 'failed';
    }
  }


async _runWithTimeout(cb: () => Promise<any>): Promise<void> {
    try {
      await this._timeoutRunner.run(cb);
    } catch (error) {
      if (!(error instanceof TimeoutRunnerError))
        throw error;
      // Do not overwrite existing failure upon hook/teardown timeout.
      if (this.status === 'passed')
        this.status = 'timedOut';
    }
    this.duration = monotonicTime() - this._startTime;
  }


async _runFn(fn: Function, skips?: 'allowSkips'): Promise<TestError | undefined> {
    try {
      await fn();
    } catch (error) {
      if (skips === 'allowSkips' && error instanceof SkipError) {
        if (this.status === 'passed')
          this.status = 'skipped';
      } else {
        const serialized = serializeError(error);
        this._failWithError(serialized, true /* isHardError */);
        return serialized;
      }
    }
  }


_addStep(data: Omit<TestStepInternal, 'complete'>) {
    return this._addStepImpl(data);
  }


_failWithError(error: TestError, isHardError: boolean) {
    // Do not overwrite any previous hard errors.
    // Some (but not all) scenarios include:
    //   - expect() that fails after uncaught exception.
    //   - fail after the timeout, e.g. due to fixture teardown.
    if (isHardError && this._hasHardError)
      return;
    if (isHardError)
      this._hasHardError = true;
    if (this.status === 'passed')
      this.status = 'failed';
    this.errors.push(error);
  }


// ------------ TestInfo methods ------------
async attach(name: string, options: { path?: string, body?: string | Buffer, contentType?: string } = {}) {
    if ((options.path !== undefined ? 1 : 0) + (options.body !== undefined ? 1 : 0) !== 1)
      throw new Error(`Exactly one of "path" and "body" must be specified`);
    if (options.path !== undefined) {
      const hash = calculateSha1(options.path);
      const dest = this.outputPath('attachments', hash + path.extname(options.path));
      await fs.promises.mkdir(path.dirname(dest), { recursive: true });
      await fs.promises.copyFile(options.path, dest);
      const contentType = options.contentType ?? (mime.getType(path.basename(options.path)) || 'application/octet-stream');
      this.attachments.push({ name, contentType, path: dest });
    } else {
      const contentType = options.contentType ?? (typeof options.body === 'string' ? 'text/plain' : 'application/octet-stream');
      this.attachments.push({ name, contentType, body: typeof options.body === 'string' ? Buffer.from(options.body) : options.body });
    }
  }


outputPath(...pathSegments: string[]){
    fs.mkdirSync(this.outputDir, { recursive: true });
    const joinedPath = path.join(...pathSegments);
    const outputPath = getContainedPath(this.outputDir, joinedPath);
    if (outputPath)
      return outputPath;
    throw new Error(`The outputPath is not allowed outside of the parent directory. Please fix the defined path.\n\n\toutputPath: ${joinedPath}`);
  }


snapshotPath(...pathSegments: string[]) {
    let suffix = '';
    const projectNamePathSegment = sanitizeForFilePath(this.project.name);
    if (projectNamePathSegment)
      suffix += '-' + projectNamePathSegment;
    if (this.snapshotSuffix)
      suffix += '-' + this.snapshotSuffix;
    const subPath = addSuffixToFilePath(path.join(...pathSegments), suffix);
    const snapshotPath =  getContainedPath(this.snapshotDir, subPath);
    if (snapshotPath)
      return snapshotPath;
    throw new Error(`The snapshotPath is not allowed outside of the parent directory. Please fix the defined path.\n\n\tsnapshotPath: ${subPath}`);
  }


skip(...args: [arg?: any, description?: string]) {
    this._modifier('skip', args);
  }


fixme(...args: [arg?: any, description?: string]) {
    this._modifier('fixme', args);
  }


fail(...args: [arg?: any, description?: string]) {
    this._modifier('fail', args);
  }


slow(...args: [arg?: any, description?: string]) {
    this._modifier('slow', args);
  }


setTimeout(timeout: number) {
    if (!this.timeout)
      return; // Zero timeout means some debug mode - do not set a timeout.
    this.timeout = timeout;
    this._timeoutRunner.updateTimeout(timeout);
  }


export function abortGameReducer(store: StoreLike, state: State, action: Action): State {

  if (state.phase !== GamePhase.FINISHED && action instanceof AbortGameAction) {

    const culprit = state.players.find(p => p.id === action.culpritId);
    if (culprit === undefined) {
      throw new GameError(GameMessage.ILLEGAL_ACTION);
    }

    // Mark all prompts as resolved, so they won't mess with our state anymore.
    state.prompts.forEach(prompt => {
      if (prompt.result === undefined) {
        prompt.result = null;
      }
    });

    // Explain why game was aborted
    switch (action.reason) {
      case AbortGameReason.TIME_ELAPSED:
        store.log(state, GameLog.LOG_TIME_ELAPSED, { name: culprit.name });
        break;
      case AbortGameReason.ILLEGAL_MOVES:
        store.log(state, GameLog.LOG_BANNED_BY_ARBITER, { name: culprit.name });
        break;
      case AbortGameReason.DISCONNECTED:
        store.log(state, GameLog.LOG_PLAYER_LEFT_THE_GAME, { name: culprit.name });
        break;
      default:
        //TODO: error catching
        break;
    }

    // Game has not started, no winner
    if (state.phase === GamePhase.WAITING_FOR_PLAYERS || state.phase === GamePhase.SETUP) {
      store.log(state, GameLog.LOG_GAME_FINISHED_BEFORE_STARTED);
      state.phase = GamePhase.FINISHED;
      state.winner = GameWinner.NONE;
      return state;
    }

    // Let's decide who wins.
    const opponent = StateUtils.getOpponent(state, culprit);
    const culpritPrizeLeft = culprit.getPrizeLeft();
    const opponentPrizeLeft = opponent.getPrizeLeft();

    // It was first turn, no winner
    if (state.turn <= 2 && culpritPrizeLeft === opponentPrizeLeft) {
      state = endGame(store, state, GameWinner.NONE);
      return state;
    }

    // Opponent has same or less prizes, he wins
    if (opponentPrizeLeft <= culpritPrizeLeft) {
      const winner = opponent === state.players[0]
        ? GameWinner.PLAYER_1
        : GameWinner.PLAYER_2;
      state = endGame(store, state, winner);
      return state;
    }

    // Otherwise it's a draw
    state = endGame(store, state, GameWinner.DRAW);
  }

  return state;
}


p => p.id === action.culpritId


prompt => {
      if (prompt.result === undefined) {
        prompt.result = null;
      }
    }


async action({ send }) {
    const { name, anime, image } = choice(waifus);
    await send.embed({
      title: name,
      description: `anime: ${anime}`,
      image: { url: choice(image) },
    });
  }


export class AutoComplete extends React.Component<AutoCompleteProps,any> {}


interface AutoCompleteProps {
    id?: string;
    value?: any;
    name?: string;
    type?: string;
    suggestions?: any[];
    field?: string;
    scrollHeight?: string;
    dropdown?: boolean;
    dropdownMode?: string;
    multiple?: boolean;
    minLength?: number;
    delay?: number;
    style?: object;
    className?: string;
    inputId?: string;
    inputStyle?: object;
    inputClassName?: string;
    placeholder?: string;
    readonly?: boolean;
    disabled?: boolean;
    maxlength?: number;
    size?: number;
    appendTo?: any;
    tabindex?: number;
    autoFocus?: boolean;
    tooltip?: any;
    tooltipOptions?: TooltipOptions;
    ariaLabelledBy?: string;
    completeMethod?(e: {originalEvent: Event, query: string}): void;
    itemTemplate?(data: any): JSX.Element | undefined;
    selectedItemTemplate?(data: any): any;
    onChange?(e: {originalEvent: Event, value: any, target: {name: string, id: string, value: any}}): void;
    onFocus?(event: Event): void;
    onBlur?(event: Event): void;
    onSelect?(e: {originalEvent: Event, value: any}): void;
    onUnselect?(e: {originalEvent: Event, value: any}): void;
    onDropdownClick?(e: {originalEvent: Event, query: string}): void;
    onClick?(event:Event): void;
    onDblClick?(event:Event): void;
    onMouseDown?(event:Event): void;
    onKeyUp?(event:Event): void;
    onKeyPress?(event:Event): void;
    onContextMenu?(event:Event): void;
    onClear?(event:Event): void;
}


async () => {
    if (!OCPIService.isConfigAvailable()) {
      testData.pending = 1;
    }
    testData.tenantContext = await ContextProvider.defaultInstance.getTenantContext(ContextDefinition.TENANT_CONTEXTS.TENANT_OCPI);
    testData.centralUserContext = testData.tenantContext.getUserContext(ContextDefinition.USER_CONTEXTS.DEFAULT_ADMIN);
    testData.userContext = testData.tenantContext.getUserContext(ContextDefinition.USER_CONTEXTS.DEFAULT_ADMIN);
    expect(testData.userContext).to.not.be.null;
    testData.centralUserService = new CentralServerService(
      testData.tenantContext.getTenant().subdomain,
      testData.centralUserContext
    );
    testData.cpoService = new OCPIService(OCPIRole.CPO);
    testData.emspService = new OCPIService(OCPIRole.EMSP);
  }


async () => {
  }


() => {
    let response;

    // Check call
    it('should access base url: /ocpi/cpo/versions', async () => {
      // Create
      response = await testData.cpoService.getVersions();
      // Check status
      expect(response.status).to.be.eql(200);
    });

    // Check Response Object
    it('should have correct OCPI Response object', () => {
      // Check structure of OCPI Structure
      testData.cpoService.checkOCPIResponseStructure(response.data);
    });

    // Check Response Value
    it('should have correct OCPI Response success value', () => {
      expect(response.data).to.not.be.empty;
      expect(response.data).to.have.property('status_code', 1000);
      expect(response.data).to.have.property('status_message', 'Success');
      expect(response.data).to.have.property('data').to.be.an('array').that.is.not.empty;
    });

    // Check structure of each version objects
    it('should contains valid Version objects', () => {
      expect(response.data.data, 'Invalid Version Object').to.satisfy((versions) => {
        for (const version of versions) {
          return expect(version).to.have.keys('version', 'url');
        }
      });
    });

    // Check at least version 2.1.1 is implemented
    it('should contains at least implementation version 2.1.1', () => {
      expect(response.data.data, 'OCPI 2.1.1 Not Available').to.satisfy((versions) => {
        let version211exist = false;

        for (const version of versions) {
          if (version.version === '2.1.1') {
            version211exist = true;
          }
        }
        return version211exist;
      });
    });
  }


async () => {
      // Create
      response = await testData.cpoService.getVersions();
      // Check status
      expect(response.status).to.be.eql(200);
    }


() => {
      // Check structure of OCPI Structure
      testData.cpoService.checkOCPIResponseStructure(response.data);
    }


() => {
      expect(response.data).to.not.be.empty;
      expect(response.data).to.have.property('status_code', 1000);
      expect(response.data).to.have.property('status_message', 'Success');
      expect(response.data).to.have.property('data').to.be.an('array').that.is.not.empty;
    }


() => {
      expect(response.data.data, 'Invalid Version Object').to.satisfy((versions) => {
        for (const version of versions) {
          return expect(version).to.have.keys('version', 'url');
        }
      });
    }


(versions) => {
        for (const version of versions) {
          return expect(version).to.have.keys('version', 'url');
        }
      }


() => {
      expect(response.data.data, 'OCPI 2.1.1 Not Available').to.satisfy((versions) => {
        let version211exist = false;

        for (const version of versions) {
          if (version.version === '2.1.1') {
            version211exist = true;
          }
        }
        return version211exist;
      });
    }


(versions) => {
        let version211exist = false;

        for (const version of versions) {
          if (version.version === '2.1.1') {
            version211exist = true;
          }
        }
        return version211exist;
      }


() => {
    let response;

    // Check call
    it('should access base url: /ocpi/emsp/versions', async () => {
      // Create
      response = await testData.emspService.getVersions();
      // Check status
      expect(response.status).to.be.eql(200);
    });

    // Check Response Object
    it('should have correct OCPI Response object', () => {
      // Check structure of OCPI Structure
      testData.emspService.checkOCPIResponseStructure(response.data);
    });

    // Check Response Value
    it('should have correct OCPI Response success value', () => {
      expect(response.data).to.not.be.empty;
      expect(response.data).to.have.property('status_code', 1000);
      expect(response.data).to.have.property('status_message', 'Success');
      expect(response.data).to.have.property('data').to.be.an('array').that.is.not.empty;
    });

    // Check structure of each version objects
    it('should contains valid Version objects', () => {
      expect(response.data.data, 'Invalid Version Object').to.satisfy((versions) => {
        for (const version of versions) {
          return expect(version).to.have.keys('version', 'url');
        }
      });
    });

    // Check at least version 2.1.1 is implemented
    it('should contains at least implementation version 2.1.1', () => {
      expect(response.data.data, 'OCPI 2.1.1 Not Available').to.satisfy((versions) => {
        let version211exist = false;

        for (const version of versions) {
          if (version.version === '2.1.1') {
            version211exist = true;
          }
        }
        return version211exist;
      });
    });
  }


async () => {
      // Create
      response = await testData.emspService.getVersions();
      // Check status
      expect(response.status).to.be.eql(200);
    }


() => {
      // Check structure of OCPI Structure
      testData.emspService.checkOCPIResponseStructure(response.data);
    }


() => {
    let response;

    // Check call
    it('should access url: /ocpi/cpo/2.1.1', async () => {
      // Create
      response = await testData.cpoService.getImplementation2_1_1();
      // Check status
      expect(response.status).to.be.eql(200);
    });

    // Check Response Object
    it('should have correct OCPI Response object', () => {
      // Check structure of OCPI Structure
      testData.cpoService.checkOCPIResponseStructure(response.data);
    });

    // Check Response Value
    it('should have correct OCPI Response success value', () => {
      expect(response.data).to.not.be.empty;
      expect(response.data).to.have.property('status_code', 1000);
      expect(response.data).to.have.property('status_message', 'Success');
      expect(response.data).to.have.property('data').to.be.an('object').that.is.not.empty;
    });

    // Check data object for Array of Endpoints
    it('should contains valid data object', () => {
      expect(response.data.data).to.have.keys('version', 'endpoints');
      expect(response.data.data.version, 'Incorrect Version').equal('2.1.1');
      expect(response.data.data.endpoints).to.be.an('array').that.is.not.empty;
    });

    // Check data object for Array of Endpoints
    it('should contains valid Endpoint objects', () => {
      expect(response.data.data.endpoints, 'Invalid Endpoints Object').to.satisfy((endpoints) => {
        let validEndpoints = true;
        for (const endpoint of endpoints) {
          validEndpoints = expect(endpoint).to.have.keys('identifier', 'url') && validEndpoints;
        }
        return validEndpoints;
      });
    });
  }


async () => {
      // Create
      response = await testData.cpoService.getImplementation2_1_1();
      // Check status
      expect(response.status).to.be.eql(200);
    }


() => {
      expect(response.data).to.not.be.empty;
      expect(response.data).to.have.property('status_code', 1000);
      expect(response.data).to.have.property('status_message', 'Success');
      expect(response.data).to.have.property('data').to.be.an('object').that.is.not.empty;
    }


() => {
      expect(response.data.data).to.have.keys('version', 'endpoints');
      expect(response.data.data.version, 'Incorrect Version').equal('2.1.1');
      expect(response.data.data.endpoints).to.be.an('array').that.is.not.empty;
    }


() => {
      expect(response.data.data.endpoints, 'Invalid Endpoints Object').to.satisfy((endpoints) => {
        let validEndpoints = true;
        for (const endpoint of endpoints) {
          validEndpoints = expect(endpoint).to.have.keys('identifier', 'url') && validEndpoints;
        }
        return validEndpoints;
      });
    }


(endpoints) => {
        let validEndpoints = true;
        for (const endpoint of endpoints) {
          validEndpoints = expect(endpoint).to.have.keys('identifier', 'url') && validEndpoints;
        }
        return validEndpoints;
      }


() => {
    let response;

    // Check call
    it('should return 501 on url: /ocpi/cpo/2.1.1/invalidEndpoint', async () => {
      // Create
      response = await testData.cpoService.accessPath('GET', '/ocpi/cpo/2.1.1/invalidEndpoint');
      // Check status
      expect(response.status).to.be.eql(501);
    });

    // Check Response Object
    it('should have correct OCPI Error Response object', () => {
      // Check structure of OCPI Structure
      testData.cpoService.checkOCPIErrorResponseStructure(response.data);
    });

    // Check Response Value
    it('should have correct OCPI Error Response value', () => {
      expect(response.data).to.not.be.empty;
      expect(response.data).to.have.property('status_code', 3000);
      expect(response.data).to.have.property('status_message', 'Endpoint invalidEndpoint not implemented');
    });
  }


async () => {
      // Create
      response = await testData.cpoService.accessPath('GET', '/ocpi/cpo/2.1.1/invalidEndpoint');
      // Check status
      expect(response.status).to.be.eql(501);
    }


() => {
      // Check structure of OCPI Structure
      testData.cpoService.checkOCPIErrorResponseStructure(response.data);
    }


() => {
      expect(response.data).to.not.be.empty;
      expect(response.data).to.have.property('status_code', 3000);
      expect(response.data).to.have.property('status_message', 'Endpoint invalidEndpoint not implemented');
    }


() => {
    let response;

    // Check call
    it('should return 501 on url: /ocpi/emsp/2.1.1/invalidEndpoint', async () => {
      // Create
      response = await testData.emspService.accessPath('GET', '/ocpi/emsp/2.1.1/invalidEndpoint');
      // Check status
      expect(response.status).to.be.eql(501);
    });

    // Check Response Object
    it('should have correct OCPI Error Response object', () => {
      // Check structure of OCPI Structure
      testData.emspService.checkOCPIErrorResponseStructure(response.data);
    });

    // Check Response Value
    it('should have correct OCPI Error Response value', () => {
      expect(response.data).to.not.be.empty;
      expect(response.data).to.have.property('status_code', 3000);
      expect(response.data).to.have.property('status_message', 'Endpoint invalidEndpoint not implemented');
    });
  }


async () => {
      // Create
      response = await testData.emspService.accessPath('GET', '/ocpi/emsp/2.1.1/invalidEndpoint');
      // Check status
      expect(response.status).to.be.eql(501);
    }


() => {
      // Check structure of OCPI Structure
      testData.emspService.checkOCPIErrorResponseStructure(response.data);
    }


() => {
    let response;

    /**
      * Access without paging
      */
    describe('Access without paging', () => {

      // Check call
      it('should access url: /ocpi/cpo/2.1.1/locations', async () => {
        // Get locations
        response = await testData.cpoService.getLocations2_1_1();
        // Check status
        expect(response.status).to.be.eql(200);
      });

      // Check Response Object
      it('should have correct OCPI Response object', () => {
        // Check structure of OCPI Structure
        testData.cpoService.checkOCPIResponseStructure(response.data);
      });

      // Check Response Value
      it('should have correct OCPI Response success value', () => {
        expect(response.data).to.not.be.empty;
        expect(response.data).to.have.property('status_code', 1000);
        expect(response.data).to.have.property('status_message', 'Success');
        expect(response.data).to.have.property('data').to.be.an('array').that.is.not.empty;
      });

      // Validate content - scan entities: locations/evses/connectors
      it('should have valid OCPI Location Entities', () => {
        expect(response.data.data, 'Invalid Location Object').to.satisfy((locations) => {
          const validLocationsAndSubEntities = true;
          // Loop through location
          for (const location of locations) {
            // Validate location
            testData.cpoService.validateLocationEntity(location);
            // ValidLocationsAndSubEntities = validLocationsAndSubEntities

            // loop through evse
            for (const evse of location.evses) {
              // Validate evse
              testData.cpoService.validateEvseEntity(evse);
              // ValidLocationsAndSubEntities = validLocationsAndSubEntities &&

              // loop through connectors
              for (const connector of evse.connectors) {
                // Validate connector
                testData.cpoService.validateConnectorEntity(connector);
                // ValidLocationsAndSubEntities = validLocationsAndSubEntities && ;
              }
            }
          }
          return validLocationsAndSubEntities;
        });
      });
    });

    /**
     * Access with paging
     */
    describe('Access with paging', () => {
      // Check access for each location
      it('should access url /ocpi/cpo/2.1.1/locations/ and have headers X-Limit and X-Total-Count', async () => {
        // Call
        const locationsResponse = await testData.cpoService.accessPath('GET', '/ocpi/cpo/2.1.1/locations');

        expect(locationsResponse.data).to.not.be.empty;
        expect(locationsResponse.data).to.have.property('status_code', 1000);
        expect(locationsResponse.data).to.have.property('status_message', 'Success');
        expect(locationsResponse.data).to.have.property('data').to.be.an('array').that.is.not.empty;
        expect(locationsResponse.headers).to.have.property('x-limit');
        expect(locationsResponse.headers).to.have.property('x-total-count');
      });

      // Check access for each location
      it('should access url with paging /ocpi/cpo/2.1.1/locations/?offset=0&limit=20', async () => {
        // Call
        const locationsResponse = await testData.cpoService.accessPath('GET', '/ocpi/cpo/2.1.1/locations/?offset=0&limit=20');

        expect(locationsResponse.data).to.not.be.empty;
        expect(locationsResponse.data).to.have.property('status_code', 1000);
        expect(locationsResponse.data).to.have.property('status_message', 'Success');
        expect(locationsResponse.data).to.have.property('data').to.be.an('array').that.is.not.empty;
      });

      // Check limit
      it('should access url with paging /ocpi/cpo/2.1.1/locations/?offset=0&limit=1', async () => {
        // Call
        const locationsResponse = await testData.cpoService.accessPath('GET', '/ocpi/cpo/2.1.1/locations/?offset=0&limit=1');

        expect(locationsResponse.data).to.not.be.empty;
        expect(locationsResponse.data).to.have.property('status_code', 1000);
        expect(locationsResponse.data).to.have.property('status_message', 'Success');
        expect(locationsResponse.data).to.have.property('data').to.be.an('array').and.to.have.length(1);
        expect(locationsResponse.headers).to.have.property('link').to.match(/^<.*:\/\/.*:.*\/ocpi\/cpo\/.*\/locations\/\?offset=1&limit=1>; rel="next"/);
      });

    });

  }


() => {

      // Check call
      it('should access url: /ocpi/cpo/2.1.1/locations', async () => {
        // Get locations
        response = await testData.cpoService.getLocations2_1_1();
        // Check status
        expect(response.status).to.be.eql(200);
      });

      // Check Response Object
      it('should have correct OCPI Response object', () => {
        // Check structure of OCPI Structure
        testData.cpoService.checkOCPIResponseStructure(response.data);
      });

      // Check Response Value
      it('should have correct OCPI Response success value', () => {
        expect(response.data).to.not.be.empty;
        expect(response.data).to.have.property('status_code', 1000);
        expect(response.data).to.have.property('status_message', 'Success');
        expect(response.data).to.have.property('data').to.be.an('array').that.is.not.empty;
      });

      // Validate content - scan entities: locations/evses/connectors
      it('should have valid OCPI Location Entities', () => {
        expect(response.data.data, 'Invalid Location Object').to.satisfy((locations) => {
          const validLocationsAndSubEntities = true;
          // Loop through location
          for (const location of locations) {
            // Validate location
            testData.cpoService.validateLocationEntity(location);
            // ValidLocationsAndSubEntities = validLocationsAndSubEntities

            // loop through evse
            for (const evse of location.evses) {
              // Validate evse
              testData.cpoService.validateEvseEntity(evse);
              // ValidLocationsAndSubEntities = validLocationsAndSubEntities &&

              // loop through connectors
              for (const connector of evse.connectors) {
                // Validate connector
                testData.cpoService.validateConnectorEntity(connector);
                // ValidLocationsAndSubEntities = validLocationsAndSubEntities && ;
              }
            }
          }
          return validLocationsAndSubEntities;
        });
      });
    }


async () => {
        // Get locations
        response = await testData.cpoService.getLocations2_1_1();
        // Check status
        expect(response.status).to.be.eql(200);
      }


() => {
        // Check structure of OCPI Structure
        testData.cpoService.checkOCPIResponseStructure(response.data);
      }


() => {
        expect(response.data).to.not.be.empty;
        expect(response.data).to.have.property('status_code', 1000);
        expect(response.data).to.have.property('status_message', 'Success');
        expect(response.data).to.have.property('data').to.be.an('array').that.is.not.empty;
      }


() => {
        expect(response.data.data, 'Invalid Location Object').to.satisfy((locations) => {
          const validLocationsAndSubEntities = true;
          // Loop through location
          for (const location of locations) {
            // Validate location
            testData.cpoService.validateLocationEntity(location);
            // ValidLocationsAndSubEntities = validLocationsAndSubEntities

            // loop through evse
            for (const evse of location.evses) {
              // Validate evse
              testData.cpoService.validateEvseEntity(evse);
              // ValidLocationsAndSubEntities = validLocationsAndSubEntities &&

              // loop through connectors
              for (const connector of evse.connectors) {
                // Validate connector
                testData.cpoService.validateConnectorEntity(connector);
                // ValidLocationsAndSubEntities = validLocationsAndSubEntities && ;
              }
            }
          }
          return validLocationsAndSubEntities;
        });
      }


(locations) => {
          const validLocationsAndSubEntities = true;
          // Loop through location
          for (const location of locations) {
            // Validate location
            testData.cpoService.validateLocationEntity(location);
            // ValidLocationsAndSubEntities = validLocationsAndSubEntities

            // loop through evse
            for (const evse of location.evses) {
              // Validate evse
              testData.cpoService.validateEvseEntity(evse);
              // ValidLocationsAndSubEntities = validLocationsAndSubEntities &&

              // loop through connectors
              for (const connector of evse.connectors) {
                // Validate connector
                testData.cpoService.validateConnectorEntity(connector);
                // ValidLocationsAndSubEntities = validLocationsAndSubEntities && ;
              }
            }
          }
          return validLocationsAndSubEntities;
        }


() => {
      // Check access for each location
      it('should access url /ocpi/cpo/2.1.1/locations/ and have headers X-Limit and X-Total-Count', async () => {
        // Call
        const locationsResponse = await testData.cpoService.accessPath('GET', '/ocpi/cpo/2.1.1/locations');

        expect(locationsResponse.data).to.not.be.empty;
        expect(locationsResponse.data).to.have.property('status_code', 1000);
        expect(locationsResponse.data).to.have.property('status_message', 'Success');
        expect(locationsResponse.data).to.have.property('data').to.be.an('array').that.is.not.empty;
        expect(locationsResponse.headers).to.have.property('x-limit');
        expect(locationsResponse.headers).to.have.property('x-total-count');
      });

      // Check access for each location
      it('should access url with paging /ocpi/cpo/2.1.1/locations/?offset=0&limit=20', async () => {
        // Call
        const locationsResponse = await testData.cpoService.accessPath('GET', '/ocpi/cpo/2.1.1/locations/?offset=0&limit=20');

        expect(locationsResponse.data).to.not.be.empty;
        expect(locationsResponse.data).to.have.property('status_code', 1000);
        expect(locationsResponse.data).to.have.property('status_message', 'Success');
        expect(locationsResponse.data).to.have.property('data').to.be.an('array').that.is.not.empty;
      });

      // Check limit
      it('should access url with paging /ocpi/cpo/2.1.1/locations/?offset=0&limit=1', async () => {
        // Call
        const locationsResponse = await testData.cpoService.accessPath('GET', '/ocpi/cpo/2.1.1/locations/?offset=0&limit=1');

        expect(locationsResponse.data).to.not.be.empty;
        expect(locationsResponse.data).to.have.property('status_code', 1000);
        expect(locationsResponse.data).to.have.property('status_message', 'Success');
        expect(locationsResponse.data).to.have.property('data').to.be.an('array').and.to.have.length(1);
        expect(locationsResponse.headers).to.have.property('link').to.match(/^<.*:\/\/.*:.*\/ocpi\/cpo\/.*\/locations\/\?offset=1&limit=1>; rel="next"/);
      });

    }


async () => {
        // Call
        const locationsResponse = await testData.cpoService.accessPath('GET', '/ocpi/cpo/2.1.1/locations');

        expect(locationsResponse.data).to.not.be.empty;
        expect(locationsResponse.data).to.have.property('status_code', 1000);
        expect(locationsResponse.data).to.have.property('status_message', 'Success');
        expect(locationsResponse.data).to.have.property('data').to.be.an('array').that.is.not.empty;
        expect(locationsResponse.headers).to.have.property('x-limit');
        expect(locationsResponse.headers).to.have.property('x-total-count');
      }


async () => {
        // Call
        const locationsResponse = await testData.cpoService.accessPath('GET', '/ocpi/cpo/2.1.1/locations/?offset=0&limit=20');

        expect(locationsResponse.data).to.not.be.empty;
        expect(locationsResponse.data).to.have.property('status_code', 1000);
        expect(locationsResponse.data).to.have.property('status_message', 'Success');
        expect(locationsResponse.data).to.have.property('data').to.be.an('array').that.is.not.empty;
      }


async () => {
        // Call
        const locationsResponse = await testData.cpoService.accessPath('GET', '/ocpi/cpo/2.1.1/locations/?offset=0&limit=1');

        expect(locationsResponse.data).to.not.be.empty;
        expect(locationsResponse.data).to.have.property('status_code', 1000);
        expect(locationsResponse.data).to.have.property('status_message', 'Success');
        expect(locationsResponse.data).to.have.property('data').to.be.an('array').and.to.have.length(1);
        expect(locationsResponse.headers).to.have.property('link').to.match(/^<.*:\/\/.*:.*\/ocpi\/cpo\/.*\/locations\/\?offset=1&limit=1>; rel="next"/);
      }


() => {
    let response;
    /**
     * Success Cases
     */
    describe('Success cases', () => {
      // Call once agian the GET Locations
      before(async function() {
        // Create
        response = await testData.cpoService.getLocations2_1_1();

        if (response.status !== 200) {
          this.skip();
        }
      });

      // Check access for each location
      it('should access single location entity /ocpi/cpo/2.1.1/locations/{locationId}', async () => {
        for (const location of response.data.data) {
          // Call
          const locationResponse = await testData.cpoService.accessPath('GET', `/ocpi/cpo/2.1.1/locations/${location.id}`);
          // Check status
          expect(locationResponse.status).to.be.eql(200);
          expect(testData.cpoService.validateLocationEntity(locationResponse.data.data));
        }
      });

      // Check access for each evse
      it('should access single EVSE entity /ocpi/cpo/2.1.1/locations/{locationId}/{evseUid}', async () => {
        for (const location of response.data.data) {
          for (const evse of location.evses) {
            // Call
            const evseResponse = await testData.cpoService.accessPath('GET', `/ocpi/cpo/2.1.1/locations/${location.id}/${evse.uid}`);
            // Check status
            expect(evseResponse.status).to.be.eql(200);
            expect(testData.cpoService.validateEvseEntity(evseResponse.data.data));
          }
        }
      });

      // Check access for each evse
      it('should access single Connector entity /ocpi/cpo/2.1.1/locations/{locationId}/{evseUid}/{connectorId}', async () => {
        for (const location of response.data.data) {
          for (const evse of location.evses) {
            for (const connector of evse.connectors) {
              // Call
              const connectorResponse = await testData.cpoService.accessPath('GET', `/ocpi/cpo/2.1.1/locations/${location.id}/${evse.uid}/${connector.id}`);
              // Check status
              expect(connectorResponse.status).to.be.eql(200);
              expect(testData.cpoService.validateConnectorEntity(connectorResponse.data.data));
            }
          }
        }
      });
    });

    /**
     * Failure cases
     */
    describe('Failure cases', () => {
      // Invalid location
      it('should not find this non-existing location  /ocpi/cpo/2.1.1/locations/5abeba9e4bae1457eb565e67', async () => {
        // Call
        const locationResponse = await testData.cpoService.accessPath('GET', '/ocpi/cpo/2.1.1/locations/5abeba9e4bae1457eb565e67');
        // Check status
        expect(locationResponse.status).to.be.eql(500);
        expect(locationResponse.data).to.have.property('timestamp');
        expect(locationResponse.data).to.have.property('status_code', 3000);
        expect(locationResponse.data).to.have.property('status_message', 'Site ID \'5abeba9e4bae1457eb565e67\' not found');
      });

      // Invalid evse uid
      it('should not find this non-existing EVSE  /ocpi/cpo/2.1.1/locations/5ce249a2372f0b1c8caf9294/NonExistingSite', async () => {
        // Call
        const locationResponse = await testData.cpoService.accessPath('GET', '/ocpi/cpo/2.1.1/locations/5ce249a2372f0b1c8caf9294/NonExistingSite');
        // Check status
        expect(locationResponse.status).to.be.eql(500);
        expect(locationResponse.data).to.have.property('timestamp');
        expect(locationResponse.data).to.have.property('status_code', 3000);
        expect(locationResponse.data).to.have.property('status_message', 'Charging Station ID not found \'NonExistingSite\' on Location ID \'5ce249a2372f0b1c8caf9294\'');
      });

      // Invalid connector id
      it('should not find this non-existing Connector  /ocpi/cpo/2.1.1/locations/5ce249a2372f0b1c8caf9294/cs-15-ut-site-withoutACL/0', async () => {
        // Call
        const locationResponse = await testData.cpoService.accessPath('GET', '/ocpi/cpo/2.1.1/locations/5ce249a2372f0b1c8caf9294/cs-15-ut-site-withoutACL/0');
        // Check status
        expect(locationResponse.status).to.be.eql(500);
        expect(locationResponse.data).to.have.property('timestamp');
        expect(locationResponse.data).to.have.property('status_code', 3000);
        expect(locationResponse.data).to.have.property('status_message', 'Connector ID \'0\' not found on Charging Station ID \'cs-15-ut-site-withoutACL\' and Location ID \'5ce249a2372f0b1c8caf9294\'');
      });
    });
  }


() => {
      // Call once agian the GET Locations
      before(async function() {
        // Create
        response = await testData.cpoService.getLocations2_1_1();

        if (response.status !== 200) {
          this.skip();
        }
      });

      // Check access for each location
      it('should access single location entity /ocpi/cpo/2.1.1/locations/{locationId}', async () => {
        for (const location of response.data.data) {
          // Call
          const locationResponse = await testData.cpoService.accessPath('GET', `/ocpi/cpo/2.1.1/locations/${location.id}`);
          // Check status
          expect(locationResponse.status).to.be.eql(200);
          expect(testData.cpoService.validateLocationEntity(locationResponse.data.data));
        }
      });

      // Check access for each evse
      it('should access single EVSE entity /ocpi/cpo/2.1.1/locations/{locationId}/{evseUid}', async () => {
        for (const location of response.data.data) {
          for (const evse of location.evses) {
            // Call
            const evseResponse = await testData.cpoService.accessPath('GET', `/ocpi/cpo/2.1.1/locations/${location.id}/${evse.uid}`);
            // Check status
            expect(evseResponse.status).to.be.eql(200);
            expect(testData.cpoService.validateEvseEntity(evseResponse.data.data));
          }
        }
      });

      // Check access for each evse
      it('should access single Connector entity /ocpi/cpo/2.1.1/locations/{locationId}/{evseUid}/{connectorId}', async () => {
        for (const location of response.data.data) {
          for (const evse of location.evses) {
            for (const connector of evse.connectors) {
              // Call
              const connectorResponse = await testData.cpoService.accessPath('GET', `/ocpi/cpo/2.1.1/locations/${location.id}/${evse.uid}/${connector.id}`);
              // Check status
              expect(connectorResponse.status).to.be.eql(200);
              expect(testData.cpoService.validateConnectorEntity(connectorResponse.data.data));
            }
          }
        }
      });
    }


async () => {
        for (const location of response.data.data) {
          // Call
          const locationResponse = await testData.cpoService.accessPath('GET', `/ocpi/cpo/2.1.1/locations/${location.id}`);
          // Check status
          expect(locationResponse.status).to.be.eql(200);
          expect(testData.cpoService.validateLocationEntity(locationResponse.data.data));
        }
      }


async () => {
        for (const location of response.data.data) {
          for (const evse of location.evses) {
            // Call
            const evseResponse = await testData.cpoService.accessPath('GET', `/ocpi/cpo/2.1.1/locations/${location.id}/${evse.uid}`);
            // Check status
            expect(evseResponse.status).to.be.eql(200);
            expect(testData.cpoService.validateEvseEntity(evseResponse.data.data));
          }
        }
      }


async () => {
        for (const location of response.data.data) {
          for (const evse of location.evses) {
            for (const connector of evse.connectors) {
              // Call
              const connectorResponse = await testData.cpoService.accessPath('GET', `/ocpi/cpo/2.1.1/locations/${location.id}/${evse.uid}/${connector.id}`);
              // Check status
              expect(connectorResponse.status).to.be.eql(200);
              expect(testData.cpoService.validateConnectorEntity(connectorResponse.data.data));
            }
          }
        }
      }


() => {
      // Invalid location
      it('should not find this non-existing location  /ocpi/cpo/2.1.1/locations/5abeba9e4bae1457eb565e67', async () => {
        // Call
        const locationResponse = await testData.cpoService.accessPath('GET', '/ocpi/cpo/2.1.1/locations/5abeba9e4bae1457eb565e67');
        // Check status
        expect(locationResponse.status).to.be.eql(500);
        expect(locationResponse.data).to.have.property('timestamp');
        expect(locationResponse.data).to.have.property('status_code', 3000);
        expect(locationResponse.data).to.have.property('status_message', 'Site ID \'5abeba9e4bae1457eb565e67\' not found');
      });

      // Invalid evse uid
      it('should not find this non-existing EVSE  /ocpi/cpo/2.1.1/locations/5ce249a2372f0b1c8caf9294/NonExistingSite', async () => {
        // Call
        const locationResponse = await testData.cpoService.accessPath('GET', '/ocpi/cpo/2.1.1/locations/5ce249a2372f0b1c8caf9294/NonExistingSite');
        // Check status
        expect(locationResponse.status).to.be.eql(500);
        expect(locationResponse.data).to.have.property('timestamp');
        expect(locationResponse.data).to.have.property('status_code', 3000);
        expect(locationResponse.data).to.have.property('status_message', 'Charging Station ID not found \'NonExistingSite\' on Location ID \'5ce249a2372f0b1c8caf9294\'');
      });

      // Invalid connector id
      it('should not find this non-existing Connector  /ocpi/cpo/2.1.1/locations/5ce249a2372f0b1c8caf9294/cs-15-ut-site-withoutACL/0', async () => {
        // Call
        const locationResponse = await testData.cpoService.accessPath('GET', '/ocpi/cpo/2.1.1/locations/5ce249a2372f0b1c8caf9294/cs-15-ut-site-withoutACL/0');
        // Check status
        expect(locationResponse.status).to.be.eql(500);
        expect(locationResponse.data).to.have.property('timestamp');
        expect(locationResponse.data).to.have.property('status_code', 3000);
        expect(locationResponse.data).to.have.property('status_message', 'Connector ID \'0\' not found on Charging Station ID \'cs-15-ut-site-withoutACL\' and Location ID \'5ce249a2372f0b1c8caf9294\'');
      });
    }


async () => {
        // Call
        const locationResponse = await testData.cpoService.accessPath('GET', '/ocpi/cpo/2.1.1/locations/5abeba9e4bae1457eb565e67');
        // Check status
        expect(locationResponse.status).to.be.eql(500);
        expect(locationResponse.data).to.have.property('timestamp');
        expect(locationResponse.data).to.have.property('status_code', 3000);
        expect(locationResponse.data).to.have.property('status_message', 'Site ID \'5abeba9e4bae1457eb565e67\' not found');
      }


async () => {
        // Call
        const locationResponse = await testData.cpoService.accessPath('GET', '/ocpi/cpo/2.1.1/locations/5ce249a2372f0b1c8caf9294/NonExistingSite');
        // Check status
        expect(locationResponse.status).to.be.eql(500);
        expect(locationResponse.data).to.have.property('timestamp');
        expect(locationResponse.data).to.have.property('status_code', 3000);
        expect(locationResponse.data).to.have.property('status_message', 'Charging Station ID not found \'NonExistingSite\' on Location ID \'5ce249a2372f0b1c8caf9294\'');
      }


async () => {
        // Call
        const locationResponse = await testData.cpoService.accessPath('GET', '/ocpi/cpo/2.1.1/locations/5ce249a2372f0b1c8caf9294/cs-15-ut-site-withoutACL/0');
        // Check status
        expect(locationResponse.status).to.be.eql(500);
        expect(locationResponse.data).to.have.property('timestamp');
        expect(locationResponse.data).to.have.property('status_code', 3000);
        expect(locationResponse.data).to.have.property('status_message', 'Connector ID \'0\' not found on Charging Station ID \'cs-15-ut-site-withoutACL\' and Location ID \'5ce249a2372f0b1c8caf9294\'');
      }


() => {
    let response;
    /**
     * Success Cases
     */
    describe('Success cases', () => {
      it('Should create a new ocpiEndpoint', async () => {
        // Check
        expect(testData.newOcpiEndpoint).to.not.be.null;
        // Create the entity
        testData.newOcpiEndpoint = await testData.centralUserService.createEntity(
          testData.centralUserService.ocpiEndpointApi, Factory.ocpiEndpoint.build());
      });

      it('Should update the ocpiEndpoint token', async () => {
        // Change entity
        testData.newOcpiEndpoint.localToken = OCPIService.getToken(OCPIRole.CPO);
        // Update
        await testData.centralUserService.updateEntity(
          testData.centralUserService.ocpiEndpointApi, testData.newOcpiEndpoint);
      });

      // // Check access for each evse
      // it('should be able to self-register', async () => {
      //   // Define credential object
      //   const credential = {
      //     'url': 'http://localhost:9090/ocpi/cpo/versions',
      //     'token': '12345',
      //     'party_id': 'SLF',
      //     'country_code': 'FR',
      //     'business_details': {
      //       'name': 'SAP Labs France',
      //       'logo': {
      //         'url': 'https://example.sap.com/img/logo.jpg',
      //         'thumbnail': 'https://example.sap.com/img/logo_thumb.jpg',
      //         'category': 'CPO',
      //         'type': 'jpeg',
      //         'width': 512,
      //         'height': 512
      //       },
      //       'website': 'http://sap.com'
      //     }
      //   };

      //   // Create
      //   response = await testData.ocpiService.postCredentials2_1_1(credential);

      //   // Check status
      //   expect(response.status).to.be.eql(200);
      //   testData.ocpiService.checkOCPIResponseStructure(response.data);
      //   expect(response.data.status_code).to.be.eql(1000);
      //   testData.ocpiService.validateCredentialEntity(response.data.data);
      // });

      it('Should delete the created ocpiEndpoint', async () => {
        // Delete the created entity
        await testData.centralUserService.deleteEntity(
          testData.centralUserService.ocpiEndpointApi, testData.newOcpiEndpoint);
      });
    });

    /**
     * Failure cases
     */
    describe('Failure cases', () => {
      // // Invalid location
      // it('should not find this non-existing location  /ocpi/cpo/2.1.1/locations/5abeba9e4bae1457eb565e67', async () => {
      //   // Call
      //   const locationResponse = await testData.ocpiService.accessPath('GET', '/ocpi/cpo/2.1.1/locations/5abeba9e4bae1457eb565e67');
      //   // Check status
      //   expect(locationResponse.status).to.be.eql(500);
      //   expect(locationResponse.data).to.have.property('timestamp');
      //   expect(locationResponse.data).to.have.property('status_code', 3000);
      //   expect(locationResponse.data).to.have.property('status_message', 'Site id \'5abeba9e4bae1457eb565e67\' not found');
      // });
    });
  }


() => {
      it('Should create a new ocpiEndpoint', async () => {
        // Check
        expect(testData.newOcpiEndpoint).to.not.be.null;
        // Create the entity
        testData.newOcpiEndpoint = await testData.centralUserService.createEntity(
          testData.centralUserService.ocpiEndpointApi, Factory.ocpiEndpoint.build());
      });

      it('Should update the ocpiEndpoint token', async () => {
        // Change entity
        testData.newOcpiEndpoint.localToken = OCPIService.getToken(OCPIRole.CPO);
        // Update
        await testData.centralUserService.updateEntity(
          testData.centralUserService.ocpiEndpointApi, testData.newOcpiEndpoint);
      });

      // // Check access for each evse
      // it('should be able to self-register', async () => {
      //   // Define credential object
      //   const credential = {
      //     'url': 'http://localhost:9090/ocpi/cpo/versions',
      //     'token': '12345',
      //     'party_id': 'SLF',
      //     'country_code': 'FR',
      //     'business_details': {
      //       'name': 'SAP Labs France',
      //       'logo': {
      //         'url': 'https://example.sap.com/img/logo.jpg',
      //         'thumbnail': 'https://example.sap.com/img/logo_thumb.jpg',
      //         'category': 'CPO',
      //         'type': 'jpeg',
      //         'width': 512,
      //         'height': 512
      //       },
      //       'website': 'http://sap.com'
      //     }
      //   };

      //   // Create
      //   response = await testData.ocpiService.postCredentials2_1_1(credential);

      //   // Check status
      //   expect(response.status).to.be.eql(200);
      //   testData.ocpiService.checkOCPIResponseStructure(response.data);
      //   expect(response.data.status_code).to.be.eql(1000);
      //   testData.ocpiService.validateCredentialEntity(response.data.data);
      // });

      it('Should delete the created ocpiEndpoint', async () => {
        // Delete the created entity
        await testData.centralUserService.deleteEntity(
          testData.centralUserService.ocpiEndpointApi, testData.newOcpiEndpoint);
      });
    }


async () => {
        // Check
        expect(testData.newOcpiEndpoint).to.not.be.null;
        // Create the entity
        testData.newOcpiEndpoint = await testData.centralUserService.createEntity(
          testData.centralUserService.ocpiEndpointApi, Factory.ocpiEndpoint.build());
      }


async () => {
        // Change entity
        testData.newOcpiEndpoint.localToken = OCPIService.getToken(OCPIRole.CPO);
        // Update
        await testData.centralUserService.updateEntity(
          testData.centralUserService.ocpiEndpointApi, testData.newOcpiEndpoint);
      }


async () => {
        // Delete the created entity
        await testData.centralUserService.deleteEntity(
          testData.centralUserService.ocpiEndpointApi, testData.newOcpiEndpoint);
      }


() => {
      // // Invalid location
      // it('should not find this non-existing location  /ocpi/cpo/2.1.1/locations/5abeba9e4bae1457eb565e67', async () => {
      //   // Call
      //   const locationResponse = await testData.ocpiService.accessPath('GET', '/ocpi/cpo/2.1.1/locations/5abeba9e4bae1457eb565e67');
      //   // Check status
      //   expect(locationResponse.status).to.be.eql(500);
      //   expect(locationResponse.data).to.have.property('timestamp');
      //   expect(locationResponse.data).to.have.property('status_code', 3000);
      //   expect(locationResponse.data).to.have.property('status_message', 'Site id \'5abeba9e4bae1457eb565e67\' not found');
      // });
    }


class TestData {
  public pending: any;
  public cpoService: OCPIService;
  public emspService: OCPIService;
  public tenantContext: TenantContext;
  public newOcpiEndpoint: any;
  public centralUserService: CentralServerService;
  public centralUserContext: any;
  public userContext: User;
}


(node: ArbolRubros<DatosNodo>) => node.data || null


(node: ArbolRubros<DatosNodo>) => !!node.children && !!node.children.length ? node.children : []


(node: ArbolRubros<DatosNodo>) => !!node.expanded


([arbol, fuente]) => {

      if (Object.keys(arbol).length !== 0) {
        if (fuente) {
          this.data = this.CargarRubros(fuente, arbol);
        } else {
          this.data = [arbol[0]];
        }
        this.dataSource = this.dataSourceBuilder.create(this.data, getters);
      } else {
        this.parametric.CargarArbolRubros('3');
      }
    }


(rubro: any) => {
      if (rubro) {
        if (Object.keys(rubro)[0] !== 'type') {
          if (rubro !== null) {
            this.selectedTreeRow = rubro;
          }
        }
      }
    }


hijo => hijo.Codigo === Fuente


@Component({
  selector: 'ngx-arbol-rubro',
  templateUrl: './arbol-rubro.component.html',
  styleUrls: ['./arbol-rubro.component.scss']
})
export class ArbolRubroComponent implements OnInit, OnDestroy, OnChanges {

  @Input() FuenteRecurso: any;

  selectedTreeRow: any = null;

  sortColumn: string = '';
  sortDirection: NbSortDirection = NbSortDirection.NONE;

  customColumn = 'Codigo';
  defaultColumns = ['Nombre'];
  allColumns = [this.customColumn, ...this.defaultColumns];

  data: ArbolRubros<DatosNodo>[];
  dataSource: NbTreeGridDataSource<any>;

  subscription$: any;
  subscription2$: any;

  FuenteRecurso$: BehaviorSubject<any>;

  constructor(
    private dataSourceBuilder: NbTreeGridDataSourceBuilder<any>,
    private store: Store<any>,
    private parametric: ParametricService,
  ) {
    this.FuenteRecurso$ = new BehaviorSubject(this.FuenteRecurso);
  }

  ngOnChanges(changes: SimpleChanges): void {

    if (changes.FuenteRecurso) {
      this.FuenteRecurso$.next(this.FuenteRecurso);
      this.store.dispatch(LoadNodoSeleccionado(null));
    }
  }

  ngOnInit() {
    const getters: NbGetters<ArbolRubros<DatosNodo>, ArbolRubros<DatosNodo>> = {
      dataGetter: (node: ArbolRubros<DatosNodo>) => node.data || null,
      childrenGetter: (node: ArbolRubros<DatosNodo>) => !!node.children && !!node.children.length ? node.children : [],
      expandedGetter: (node: ArbolRubros<DatosNodo>) => !!node.expanded,
    };
    this.subscription$ = combineLatest([
      this.store.select(getArbolRubro),
      this.FuenteRecurso$.asObservable(),
    ]).subscribe(([arbol, fuente]) => {

      if (Object.keys(arbol).length !== 0) {
        if (fuente) {
          this.data = this.CargarRubros(fuente, arbol);
        } else {
          this.data = [arbol[0]];
        }
        this.dataSource = this.dataSourceBuilder.create(this.data, getters);
      } else {
        this.parametric.CargarArbolRubros('3');
      }
    });
    this.subscription2$ = this.store.select(getNodoSeleccionado).subscribe((rubro: any) => {
      if (rubro) {
        if (Object.keys(rubro)[0] !== 'type') {
          if (rubro !== null) {
            this.selectedTreeRow = rubro;
          }
        }
      }
    });
  }

  changeSort(sortRequest: NbSortRequest): void {
    this.dataSource.sort(sortRequest);
    this.sortColumn = sortRequest.column;
    this.sortDirection = sortRequest.direction;
  }

  getDirection(column: string): NbSortDirection {
    if (column === this.sortColumn) {
      return this.sortDirection;
    }
    return NbSortDirection.NONE;
  }

  ngOnDestroy(): void {
    this.subscription$.unsubscribe();
    this.subscription2$.unsubscribe();
  }

  onSelect(row: any) {

    this.store.dispatch(LoadNodoSeleccionado(row));
  }

  CargarRubros(Fuente: any, Arbol: any) {
    // console.log(Arbol[0]);
    const ArbolFuenteRecurso = Arbol[0].children.find(
      hijo => hijo.Codigo === Fuente
    );

    return [ArbolFuenteRecurso];
  }
}


ngOnChanges(changes: SimpleChanges): void {

    if (changes.FuenteRecurso) {
      this.FuenteRecurso$.next(this.FuenteRecurso);
      this.store.dispatch(LoadNodoSeleccionado(null));
    }
  }


ngOnInit() {
    const getters: NbGetters<ArbolRubros<DatosNodo>, ArbolRubros<DatosNodo>> = {
      dataGetter: (node: ArbolRubros<DatosNodo>) => node.data || null,
      childrenGetter: (node: ArbolRubros<DatosNodo>) => !!node.children && !!node.children.length ? node.children : [],
      expandedGetter: (node: ArbolRubros<DatosNodo>) => !!node.expanded,
    };
    this.subscription$ = combineLatest([
      this.store.select(getArbolRubro),
      this.FuenteRecurso$.asObservable(),
    ]).subscribe(([arbol, fuente]) => {

      if (Object.keys(arbol).length !== 0) {
        if (fuente) {
          this.data = this.CargarRubros(fuente, arbol);
        } else {
          this.data = [arbol[0]];
        }
        this.dataSource = this.dataSourceBuilder.create(this.data, getters);
      } else {
        this.parametric.CargarArbolRubros('3');
      }
    });
    this.subscription2$ = this.store.select(getNodoSeleccionado).subscribe((rubro: any) => {
      if (rubro) {
        if (Object.keys(rubro)[0] !== 'type') {
          if (rubro !== null) {
            this.selectedTreeRow = rubro;
          }
        }
      }
    });
  }


changeSort(sortRequest: NbSortRequest): void {
    this.dataSource.sort(sortRequest);
    this.sortColumn = sortRequest.column;
    this.sortDirection = sortRequest.direction;
  }


getDirection(column: string): NbSortDirection {
    if (column === this.sortColumn) {
      return this.sortDirection;
    }
    return NbSortDirection.NONE;
  }


ngOnDestroy(): void {
    this.subscription$.unsubscribe();
    this.subscription2$.unsubscribe();
  }


onSelect(row: any) {

    this.store.dispatch(LoadNodoSeleccionado(row));
  }


CargarRubros(Fuente: any, Arbol: any) {
    // console.log(Arbol[0]);
    const ArbolFuenteRecurso = Arbol[0].children.find(
      hijo => hijo.Codigo === Fuente
    );

    return [ArbolFuenteRecurso];
  }


(abort: () => never): Promise<Buffer> => {
  const { stdin } = process
  const ret: any[] = []
  let len = 0

  return new Promise(resolve => {
    if (stdin.isTTY) {
      abort()
    }

    stdin.on('readable', () => {
      let chunk

      while ((chunk = stdin.read())) {
        ret.push(chunk)
        len += chunk.length
      }
    })

    stdin.on('end', () => {
      resolve(Buffer.concat(ret, len))
    })
  })
}


resolve => {
    if (stdin.isTTY) {
      abort()
    }

    stdin.on('readable', () => {
      let chunk

      while ((chunk = stdin.read())) {
        ret.push(chunk)
        len += chunk.length
      }
    })

    stdin.on('end', () => {
      resolve(Buffer.concat(ret, len))
    })
  }


() => {
      let chunk

      while ((chunk = stdin.read())) {
        ret.push(chunk)
        len += chunk.length
      }
    }


() => {
      resolve(Buffer.concat(ret, len))
    }


(options: Options) => {
  const { verbose, api, abort, file } = options
  const dagApi = DagApi.of(api)
  const bufferP = file === undefined ? stdinToBuffer(abort) : Promise.resolve(fs.readFileSync(file))

  bufferP
    .then(buffer => {
      const text = buffer.toString('utf8')
      const json = JSON.parse(text)

      if (!Array.isArray(json)) {
        console.log('must be array!')
        process.exit(2)
      }

      const columns = ColumnMap.of(json)

      return compressAndStore(dagApi.put)(columns).then(result => {
        if (verbose > 0) {
          console.log('Input size      ', buffer.length)
          console.log('Compressed size ', result.stats.size)
          console.log('Ratio           ', (buffer.length / result.stats.size).toFixed(2))
          console.log('IPFS dag objects', result.stats.blocks)
        }
        console.log(result.link['/'])
      })
    })
    .catch(error => {
      console.error('', error)
      process.exit(4)
    })
}